roojs-all.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  * @param {Object} cfg Configuration
31375  */
31376 Roo.tree.DefaultSelectionModel = function(cfg){
31377    this.selNode = null;
31378    
31379    
31380    
31381    this.addEvents({
31382        /**
31383         * @event selectionchange
31384         * Fires when the selected node changes
31385         * @param {DefaultSelectionModel} this
31386         * @param {TreeNode} node the new selection
31387         */
31388        "selectionchange" : true,
31389
31390        /**
31391         * @event beforeselect
31392         * Fires before the selected node changes, return false to cancel the change
31393         * @param {DefaultSelectionModel} this
31394         * @param {TreeNode} node the new selection
31395         * @param {TreeNode} node the old selection
31396         */
31397        "beforeselect" : true
31398    });
31399    
31400     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31401 };
31402
31403 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31404     init : function(tree){
31405         this.tree = tree;
31406         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31407         tree.on("click", this.onNodeClick, this);
31408     },
31409     
31410     onNodeClick : function(node, e){
31411         if (e.ctrlKey && this.selNode == node)  {
31412             this.unselect(node);
31413             return;
31414         }
31415         this.select(node);
31416     },
31417     
31418     /**
31419      * Select a node.
31420      * @param {TreeNode} node The node to select
31421      * @return {TreeNode} The selected node
31422      */
31423     select : function(node){
31424         var last = this.selNode;
31425         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31426             if(last){
31427                 last.ui.onSelectedChange(false);
31428             }
31429             this.selNode = node;
31430             node.ui.onSelectedChange(true);
31431             this.fireEvent("selectionchange", this, node, last);
31432         }
31433         return node;
31434     },
31435     
31436     /**
31437      * Deselect a node.
31438      * @param {TreeNode} node The node to unselect
31439      */
31440     unselect : function(node){
31441         if(this.selNode == node){
31442             this.clearSelections();
31443         }    
31444     },
31445     
31446     /**
31447      * Clear all selections
31448      */
31449     clearSelections : function(){
31450         var n = this.selNode;
31451         if(n){
31452             n.ui.onSelectedChange(false);
31453             this.selNode = null;
31454             this.fireEvent("selectionchange", this, null);
31455         }
31456         return n;
31457     },
31458     
31459     /**
31460      * Get the selected node
31461      * @return {TreeNode} The selected node
31462      */
31463     getSelectedNode : function(){
31464         return this.selNode;    
31465     },
31466     
31467     /**
31468      * Returns true if the node is selected
31469      * @param {TreeNode} node The node to check
31470      * @return {Boolean}
31471      */
31472     isSelected : function(node){
31473         return this.selNode == node;  
31474     },
31475
31476     /**
31477      * Selects the node above the selected node in the tree, intelligently walking the nodes
31478      * @return TreeNode The new selection
31479      */
31480     selectPrevious : function(){
31481         var s = this.selNode || this.lastSelNode;
31482         if(!s){
31483             return null;
31484         }
31485         var ps = s.previousSibling;
31486         if(ps){
31487             if(!ps.isExpanded() || ps.childNodes.length < 1){
31488                 return this.select(ps);
31489             } else{
31490                 var lc = ps.lastChild;
31491                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31492                     lc = lc.lastChild;
31493                 }
31494                 return this.select(lc);
31495             }
31496         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31497             return this.select(s.parentNode);
31498         }
31499         return null;
31500     },
31501
31502     /**
31503      * Selects the node above the selected node in the tree, intelligently walking the nodes
31504      * @return TreeNode The new selection
31505      */
31506     selectNext : function(){
31507         var s = this.selNode || this.lastSelNode;
31508         if(!s){
31509             return null;
31510         }
31511         if(s.firstChild && s.isExpanded()){
31512              return this.select(s.firstChild);
31513          }else if(s.nextSibling){
31514              return this.select(s.nextSibling);
31515          }else if(s.parentNode){
31516             var newS = null;
31517             s.parentNode.bubble(function(){
31518                 if(this.nextSibling){
31519                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31520                     return false;
31521                 }
31522             });
31523             return newS;
31524          }
31525         return null;
31526     },
31527
31528     onKeyDown : function(e){
31529         var s = this.selNode || this.lastSelNode;
31530         // undesirable, but required
31531         var sm = this;
31532         if(!s){
31533             return;
31534         }
31535         var k = e.getKey();
31536         switch(k){
31537              case e.DOWN:
31538                  e.stopEvent();
31539                  this.selectNext();
31540              break;
31541              case e.UP:
31542                  e.stopEvent();
31543                  this.selectPrevious();
31544              break;
31545              case e.RIGHT:
31546                  e.preventDefault();
31547                  if(s.hasChildNodes()){
31548                      if(!s.isExpanded()){
31549                          s.expand();
31550                      }else if(s.firstChild){
31551                          this.select(s.firstChild, e);
31552                      }
31553                  }
31554              break;
31555              case e.LEFT:
31556                  e.preventDefault();
31557                  if(s.hasChildNodes() && s.isExpanded()){
31558                      s.collapse();
31559                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31560                      this.select(s.parentNode, e);
31561                  }
31562              break;
31563         };
31564     }
31565 });
31566
31567 /**
31568  * @class Roo.tree.MultiSelectionModel
31569  * @extends Roo.util.Observable
31570  * Multi selection for a TreePanel.
31571  * @param {Object} cfg Configuration
31572  */
31573 Roo.tree.MultiSelectionModel = function(){
31574    this.selNodes = [];
31575    this.selMap = {};
31576    this.addEvents({
31577        /**
31578         * @event selectionchange
31579         * Fires when the selected nodes change
31580         * @param {MultiSelectionModel} this
31581         * @param {Array} nodes Array of the selected nodes
31582         */
31583        "selectionchange" : true
31584    });
31585    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31586    
31587 };
31588
31589 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31590     init : function(tree){
31591         this.tree = tree;
31592         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31593         tree.on("click", this.onNodeClick, this);
31594     },
31595     
31596     onNodeClick : function(node, e){
31597         this.select(node, e, e.ctrlKey);
31598     },
31599     
31600     /**
31601      * Select a node.
31602      * @param {TreeNode} node The node to select
31603      * @param {EventObject} e (optional) An event associated with the selection
31604      * @param {Boolean} keepExisting True to retain existing selections
31605      * @return {TreeNode} The selected node
31606      */
31607     select : function(node, e, keepExisting){
31608         if(keepExisting !== true){
31609             this.clearSelections(true);
31610         }
31611         if(this.isSelected(node)){
31612             this.lastSelNode = node;
31613             return node;
31614         }
31615         this.selNodes.push(node);
31616         this.selMap[node.id] = node;
31617         this.lastSelNode = node;
31618         node.ui.onSelectedChange(true);
31619         this.fireEvent("selectionchange", this, this.selNodes);
31620         return node;
31621     },
31622     
31623     /**
31624      * Deselect a node.
31625      * @param {TreeNode} node The node to unselect
31626      */
31627     unselect : function(node){
31628         if(this.selMap[node.id]){
31629             node.ui.onSelectedChange(false);
31630             var sn = this.selNodes;
31631             var index = -1;
31632             if(sn.indexOf){
31633                 index = sn.indexOf(node);
31634             }else{
31635                 for(var i = 0, len = sn.length; i < len; i++){
31636                     if(sn[i] == node){
31637                         index = i;
31638                         break;
31639                     }
31640                 }
31641             }
31642             if(index != -1){
31643                 this.selNodes.splice(index, 1);
31644             }
31645             delete this.selMap[node.id];
31646             this.fireEvent("selectionchange", this, this.selNodes);
31647         }
31648     },
31649     
31650     /**
31651      * Clear all selections
31652      */
31653     clearSelections : function(suppressEvent){
31654         var sn = this.selNodes;
31655         if(sn.length > 0){
31656             for(var i = 0, len = sn.length; i < len; i++){
31657                 sn[i].ui.onSelectedChange(false);
31658             }
31659             this.selNodes = [];
31660             this.selMap = {};
31661             if(suppressEvent !== true){
31662                 this.fireEvent("selectionchange", this, this.selNodes);
31663             }
31664         }
31665     },
31666     
31667     /**
31668      * Returns true if the node is selected
31669      * @param {TreeNode} node The node to check
31670      * @return {Boolean}
31671      */
31672     isSelected : function(node){
31673         return this.selMap[node.id] ? true : false;  
31674     },
31675     
31676     /**
31677      * Returns an array of the selected nodes
31678      * @return {Array}
31679      */
31680     getSelectedNodes : function(){
31681         return this.selNodes;    
31682     },
31683
31684     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31685
31686     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31687
31688     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31689 });/*
31690  * Based on:
31691  * Ext JS Library 1.1.1
31692  * Copyright(c) 2006-2007, Ext JS, LLC.
31693  *
31694  * Originally Released Under LGPL - original licence link has changed is not relivant.
31695  *
31696  * Fork - LGPL
31697  * <script type="text/javascript">
31698  */
31699  
31700 /**
31701  * @class Roo.tree.TreeNode
31702  * @extends Roo.data.Node
31703  * @cfg {String} text The text for this node
31704  * @cfg {Boolean} expanded true to start the node expanded
31705  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31706  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31707  * @cfg {Boolean} disabled true to start the node disabled
31708  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31709  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31710  * @cfg {String} cls A css class to be added to the node
31711  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31712  * @cfg {String} href URL of the link used for the node (defaults to #)
31713  * @cfg {String} hrefTarget target frame for the link
31714  * @cfg {String} qtip An Ext QuickTip for the node
31715  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31716  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31717  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31718  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31719  * (defaults to undefined with no checkbox rendered)
31720  * @constructor
31721  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31722  */
31723 Roo.tree.TreeNode = function(attributes){
31724     attributes = attributes || {};
31725     if(typeof attributes == "string"){
31726         attributes = {text: attributes};
31727     }
31728     this.childrenRendered = false;
31729     this.rendered = false;
31730     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31731     this.expanded = attributes.expanded === true;
31732     this.isTarget = attributes.isTarget !== false;
31733     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31734     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31735
31736     /**
31737      * Read-only. The text for this node. To change it use setText().
31738      * @type String
31739      */
31740     this.text = attributes.text;
31741     /**
31742      * True if this node is disabled.
31743      * @type Boolean
31744      */
31745     this.disabled = attributes.disabled === true;
31746
31747     this.addEvents({
31748         /**
31749         * @event textchange
31750         * Fires when the text for this node is changed
31751         * @param {Node} this This node
31752         * @param {String} text The new text
31753         * @param {String} oldText The old text
31754         */
31755         "textchange" : true,
31756         /**
31757         * @event beforeexpand
31758         * Fires before this node is expanded, return false to cancel.
31759         * @param {Node} this This node
31760         * @param {Boolean} deep
31761         * @param {Boolean} anim
31762         */
31763         "beforeexpand" : true,
31764         /**
31765         * @event beforecollapse
31766         * Fires before this node is collapsed, return false to cancel.
31767         * @param {Node} this This node
31768         * @param {Boolean} deep
31769         * @param {Boolean} anim
31770         */
31771         "beforecollapse" : true,
31772         /**
31773         * @event expand
31774         * Fires when this node is expanded
31775         * @param {Node} this This node
31776         */
31777         "expand" : true,
31778         /**
31779         * @event disabledchange
31780         * Fires when the disabled status of this node changes
31781         * @param {Node} this This node
31782         * @param {Boolean} disabled
31783         */
31784         "disabledchange" : true,
31785         /**
31786         * @event collapse
31787         * Fires when this node is collapsed
31788         * @param {Node} this This node
31789         */
31790         "collapse" : true,
31791         /**
31792         * @event beforeclick
31793         * Fires before click processing. Return false to cancel the default action.
31794         * @param {Node} this This node
31795         * @param {Roo.EventObject} e The event object
31796         */
31797         "beforeclick":true,
31798         /**
31799         * @event checkchange
31800         * Fires when a node with a checkbox's checked property changes
31801         * @param {Node} this This node
31802         * @param {Boolean} checked
31803         */
31804         "checkchange":true,
31805         /**
31806         * @event click
31807         * Fires when this node is clicked
31808         * @param {Node} this This node
31809         * @param {Roo.EventObject} e The event object
31810         */
31811         "click":true,
31812         /**
31813         * @event dblclick
31814         * Fires when this node is double clicked
31815         * @param {Node} this This node
31816         * @param {Roo.EventObject} e The event object
31817         */
31818         "dblclick":true,
31819         /**
31820         * @event contextmenu
31821         * Fires when this node is right clicked
31822         * @param {Node} this This node
31823         * @param {Roo.EventObject} e The event object
31824         */
31825         "contextmenu":true,
31826         /**
31827         * @event beforechildrenrendered
31828         * Fires right before the child nodes for this node are rendered
31829         * @param {Node} this This node
31830         */
31831         "beforechildrenrendered":true
31832     });
31833
31834     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31835
31836     /**
31837      * Read-only. The UI for this node
31838      * @type TreeNodeUI
31839      */
31840     this.ui = new uiClass(this);
31841 };
31842 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31843     preventHScroll: true,
31844     /**
31845      * Returns true if this node is expanded
31846      * @return {Boolean}
31847      */
31848     isExpanded : function(){
31849         return this.expanded;
31850     },
31851
31852     /**
31853      * Returns the UI object for this node
31854      * @return {TreeNodeUI}
31855      */
31856     getUI : function(){
31857         return this.ui;
31858     },
31859
31860     // private override
31861     setFirstChild : function(node){
31862         var of = this.firstChild;
31863         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31864         if(this.childrenRendered && of && node != of){
31865             of.renderIndent(true, true);
31866         }
31867         if(this.rendered){
31868             this.renderIndent(true, true);
31869         }
31870     },
31871
31872     // private override
31873     setLastChild : function(node){
31874         var ol = this.lastChild;
31875         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31876         if(this.childrenRendered && ol && node != ol){
31877             ol.renderIndent(true, true);
31878         }
31879         if(this.rendered){
31880             this.renderIndent(true, true);
31881         }
31882     },
31883
31884     // these methods are overridden to provide lazy rendering support
31885     // private override
31886     appendChild : function(){
31887         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31888         if(node && this.childrenRendered){
31889             node.render();
31890         }
31891         this.ui.updateExpandIcon();
31892         return node;
31893     },
31894
31895     // private override
31896     removeChild : function(node){
31897         this.ownerTree.getSelectionModel().unselect(node);
31898         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31899         // if it's been rendered remove dom node
31900         if(this.childrenRendered){
31901             node.ui.remove();
31902         }
31903         if(this.childNodes.length < 1){
31904             this.collapse(false, false);
31905         }else{
31906             this.ui.updateExpandIcon();
31907         }
31908         if(!this.firstChild) {
31909             this.childrenRendered = false;
31910         }
31911         return node;
31912     },
31913
31914     // private override
31915     insertBefore : function(node, refNode){
31916         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31917         if(newNode && refNode && this.childrenRendered){
31918             node.render();
31919         }
31920         this.ui.updateExpandIcon();
31921         return newNode;
31922     },
31923
31924     /**
31925      * Sets the text for this node
31926      * @param {String} text
31927      */
31928     setText : function(text){
31929         var oldText = this.text;
31930         this.text = text;
31931         this.attributes.text = text;
31932         if(this.rendered){ // event without subscribing
31933             this.ui.onTextChange(this, text, oldText);
31934         }
31935         this.fireEvent("textchange", this, text, oldText);
31936     },
31937
31938     /**
31939      * Triggers selection of this node
31940      */
31941     select : function(){
31942         this.getOwnerTree().getSelectionModel().select(this);
31943     },
31944
31945     /**
31946      * Triggers deselection of this node
31947      */
31948     unselect : function(){
31949         this.getOwnerTree().getSelectionModel().unselect(this);
31950     },
31951
31952     /**
31953      * Returns true if this node is selected
31954      * @return {Boolean}
31955      */
31956     isSelected : function(){
31957         return this.getOwnerTree().getSelectionModel().isSelected(this);
31958     },
31959
31960     /**
31961      * Expand this node.
31962      * @param {Boolean} deep (optional) True to expand all children as well
31963      * @param {Boolean} anim (optional) false to cancel the default animation
31964      * @param {Function} callback (optional) A callback to be called when
31965      * expanding this node completes (does not wait for deep expand to complete).
31966      * Called with 1 parameter, this node.
31967      */
31968     expand : function(deep, anim, callback){
31969         if(!this.expanded){
31970             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31971                 return;
31972             }
31973             if(!this.childrenRendered){
31974                 this.renderChildren();
31975             }
31976             this.expanded = true;
31977             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31978                 this.ui.animExpand(function(){
31979                     this.fireEvent("expand", this);
31980                     if(typeof callback == "function"){
31981                         callback(this);
31982                     }
31983                     if(deep === true){
31984                         this.expandChildNodes(true);
31985                     }
31986                 }.createDelegate(this));
31987                 return;
31988             }else{
31989                 this.ui.expand();
31990                 this.fireEvent("expand", this);
31991                 if(typeof callback == "function"){
31992                     callback(this);
31993                 }
31994             }
31995         }else{
31996            if(typeof callback == "function"){
31997                callback(this);
31998            }
31999         }
32000         if(deep === true){
32001             this.expandChildNodes(true);
32002         }
32003     },
32004
32005     isHiddenRoot : function(){
32006         return this.isRoot && !this.getOwnerTree().rootVisible;
32007     },
32008
32009     /**
32010      * Collapse this node.
32011      * @param {Boolean} deep (optional) True to collapse all children as well
32012      * @param {Boolean} anim (optional) false to cancel the default animation
32013      */
32014     collapse : function(deep, anim){
32015         if(this.expanded && !this.isHiddenRoot()){
32016             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32017                 return;
32018             }
32019             this.expanded = false;
32020             if((this.getOwnerTree().animate && anim !== false) || anim){
32021                 this.ui.animCollapse(function(){
32022                     this.fireEvent("collapse", this);
32023                     if(deep === true){
32024                         this.collapseChildNodes(true);
32025                     }
32026                 }.createDelegate(this));
32027                 return;
32028             }else{
32029                 this.ui.collapse();
32030                 this.fireEvent("collapse", this);
32031             }
32032         }
32033         if(deep === true){
32034             var cs = this.childNodes;
32035             for(var i = 0, len = cs.length; i < len; i++) {
32036                 cs[i].collapse(true, false);
32037             }
32038         }
32039     },
32040
32041     // private
32042     delayedExpand : function(delay){
32043         if(!this.expandProcId){
32044             this.expandProcId = this.expand.defer(delay, this);
32045         }
32046     },
32047
32048     // private
32049     cancelExpand : function(){
32050         if(this.expandProcId){
32051             clearTimeout(this.expandProcId);
32052         }
32053         this.expandProcId = false;
32054     },
32055
32056     /**
32057      * Toggles expanded/collapsed state of the node
32058      */
32059     toggle : function(){
32060         if(this.expanded){
32061             this.collapse();
32062         }else{
32063             this.expand();
32064         }
32065     },
32066
32067     /**
32068      * Ensures all parent nodes are expanded
32069      */
32070     ensureVisible : function(callback){
32071         var tree = this.getOwnerTree();
32072         tree.expandPath(this.parentNode.getPath(), false, function(){
32073             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32074             Roo.callback(callback);
32075         }.createDelegate(this));
32076     },
32077
32078     /**
32079      * Expand all child nodes
32080      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32081      */
32082     expandChildNodes : function(deep){
32083         var cs = this.childNodes;
32084         for(var i = 0, len = cs.length; i < len; i++) {
32085                 cs[i].expand(deep);
32086         }
32087     },
32088
32089     /**
32090      * Collapse all child nodes
32091      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32092      */
32093     collapseChildNodes : function(deep){
32094         var cs = this.childNodes;
32095         for(var i = 0, len = cs.length; i < len; i++) {
32096                 cs[i].collapse(deep);
32097         }
32098     },
32099
32100     /**
32101      * Disables this node
32102      */
32103     disable : function(){
32104         this.disabled = true;
32105         this.unselect();
32106         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32107             this.ui.onDisableChange(this, true);
32108         }
32109         this.fireEvent("disabledchange", this, true);
32110     },
32111
32112     /**
32113      * Enables this node
32114      */
32115     enable : function(){
32116         this.disabled = false;
32117         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32118             this.ui.onDisableChange(this, false);
32119         }
32120         this.fireEvent("disabledchange", this, false);
32121     },
32122
32123     // private
32124     renderChildren : function(suppressEvent){
32125         if(suppressEvent !== false){
32126             this.fireEvent("beforechildrenrendered", this);
32127         }
32128         var cs = this.childNodes;
32129         for(var i = 0, len = cs.length; i < len; i++){
32130             cs[i].render(true);
32131         }
32132         this.childrenRendered = true;
32133     },
32134
32135     // private
32136     sort : function(fn, scope){
32137         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32138         if(this.childrenRendered){
32139             var cs = this.childNodes;
32140             for(var i = 0, len = cs.length; i < len; i++){
32141                 cs[i].render(true);
32142             }
32143         }
32144     },
32145
32146     // private
32147     render : function(bulkRender){
32148         this.ui.render(bulkRender);
32149         if(!this.rendered){
32150             this.rendered = true;
32151             if(this.expanded){
32152                 this.expanded = false;
32153                 this.expand(false, false);
32154             }
32155         }
32156     },
32157
32158     // private
32159     renderIndent : function(deep, refresh){
32160         if(refresh){
32161             this.ui.childIndent = null;
32162         }
32163         this.ui.renderIndent();
32164         if(deep === true && this.childrenRendered){
32165             var cs = this.childNodes;
32166             for(var i = 0, len = cs.length; i < len; i++){
32167                 cs[i].renderIndent(true, refresh);
32168             }
32169         }
32170     }
32171 });/*
32172  * Based on:
32173  * Ext JS Library 1.1.1
32174  * Copyright(c) 2006-2007, Ext JS, LLC.
32175  *
32176  * Originally Released Under LGPL - original licence link has changed is not relivant.
32177  *
32178  * Fork - LGPL
32179  * <script type="text/javascript">
32180  */
32181  
32182 /**
32183  * @class Roo.tree.AsyncTreeNode
32184  * @extends Roo.tree.TreeNode
32185  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32186  * @constructor
32187  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32188  */
32189  Roo.tree.AsyncTreeNode = function(config){
32190     this.loaded = false;
32191     this.loading = false;
32192     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32193     /**
32194     * @event beforeload
32195     * Fires before this node is loaded, return false to cancel
32196     * @param {Node} this This node
32197     */
32198     this.addEvents({'beforeload':true, 'load': true});
32199     /**
32200     * @event load
32201     * Fires when this node is loaded
32202     * @param {Node} this This node
32203     */
32204     /**
32205      * The loader used by this node (defaults to using the tree's defined loader)
32206      * @type TreeLoader
32207      * @property loader
32208      */
32209 };
32210 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32211     expand : function(deep, anim, callback){
32212         if(this.loading){ // if an async load is already running, waiting til it's done
32213             var timer;
32214             var f = function(){
32215                 if(!this.loading){ // done loading
32216                     clearInterval(timer);
32217                     this.expand(deep, anim, callback);
32218                 }
32219             }.createDelegate(this);
32220             timer = setInterval(f, 200);
32221             return;
32222         }
32223         if(!this.loaded){
32224             if(this.fireEvent("beforeload", this) === false){
32225                 return;
32226             }
32227             this.loading = true;
32228             this.ui.beforeLoad(this);
32229             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32230             if(loader){
32231                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32232                 return;
32233             }
32234         }
32235         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32236     },
32237     
32238     /**
32239      * Returns true if this node is currently loading
32240      * @return {Boolean}
32241      */
32242     isLoading : function(){
32243         return this.loading;  
32244     },
32245     
32246     loadComplete : function(deep, anim, callback){
32247         this.loading = false;
32248         this.loaded = true;
32249         this.ui.afterLoad(this);
32250         this.fireEvent("load", this);
32251         this.expand(deep, anim, callback);
32252     },
32253     
32254     /**
32255      * Returns true if this node has been loaded
32256      * @return {Boolean}
32257      */
32258     isLoaded : function(){
32259         return this.loaded;
32260     },
32261     
32262     hasChildNodes : function(){
32263         if(!this.isLeaf() && !this.loaded){
32264             return true;
32265         }else{
32266             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32267         }
32268     },
32269
32270     /**
32271      * Trigger a reload for this node
32272      * @param {Function} callback
32273      */
32274     reload : function(callback){
32275         this.collapse(false, false);
32276         while(this.firstChild){
32277             this.removeChild(this.firstChild);
32278         }
32279         this.childrenRendered = false;
32280         this.loaded = false;
32281         if(this.isHiddenRoot()){
32282             this.expanded = false;
32283         }
32284         this.expand(false, false, callback);
32285     }
32286 });/*
32287  * Based on:
32288  * Ext JS Library 1.1.1
32289  * Copyright(c) 2006-2007, Ext JS, LLC.
32290  *
32291  * Originally Released Under LGPL - original licence link has changed is not relivant.
32292  *
32293  * Fork - LGPL
32294  * <script type="text/javascript">
32295  */
32296  
32297 /**
32298  * @class Roo.tree.TreeNodeUI
32299  * @constructor
32300  * @param {Object} node The node to render
32301  * The TreeNode UI implementation is separate from the
32302  * tree implementation. Unless you are customizing the tree UI,
32303  * you should never have to use this directly.
32304  */
32305 Roo.tree.TreeNodeUI = function(node){
32306     this.node = node;
32307     this.rendered = false;
32308     this.animating = false;
32309     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32310 };
32311
32312 Roo.tree.TreeNodeUI.prototype = {
32313     removeChild : function(node){
32314         if(this.rendered){
32315             this.ctNode.removeChild(node.ui.getEl());
32316         }
32317     },
32318
32319     beforeLoad : function(){
32320          this.addClass("x-tree-node-loading");
32321     },
32322
32323     afterLoad : function(){
32324          this.removeClass("x-tree-node-loading");
32325     },
32326
32327     onTextChange : function(node, text, oldText){
32328         if(this.rendered){
32329             this.textNode.innerHTML = text;
32330         }
32331     },
32332
32333     onDisableChange : function(node, state){
32334         this.disabled = state;
32335         if(state){
32336             this.addClass("x-tree-node-disabled");
32337         }else{
32338             this.removeClass("x-tree-node-disabled");
32339         }
32340     },
32341
32342     onSelectedChange : function(state){
32343         if(state){
32344             this.focus();
32345             this.addClass("x-tree-selected");
32346         }else{
32347             //this.blur();
32348             this.removeClass("x-tree-selected");
32349         }
32350     },
32351
32352     onMove : function(tree, node, oldParent, newParent, index, refNode){
32353         this.childIndent = null;
32354         if(this.rendered){
32355             var targetNode = newParent.ui.getContainer();
32356             if(!targetNode){//target not rendered
32357                 this.holder = document.createElement("div");
32358                 this.holder.appendChild(this.wrap);
32359                 return;
32360             }
32361             var insertBefore = refNode ? refNode.ui.getEl() : null;
32362             if(insertBefore){
32363                 targetNode.insertBefore(this.wrap, insertBefore);
32364             }else{
32365                 targetNode.appendChild(this.wrap);
32366             }
32367             this.node.renderIndent(true);
32368         }
32369     },
32370
32371     addClass : function(cls){
32372         if(this.elNode){
32373             Roo.fly(this.elNode).addClass(cls);
32374         }
32375     },
32376
32377     removeClass : function(cls){
32378         if(this.elNode){
32379             Roo.fly(this.elNode).removeClass(cls);
32380         }
32381     },
32382
32383     remove : function(){
32384         if(this.rendered){
32385             this.holder = document.createElement("div");
32386             this.holder.appendChild(this.wrap);
32387         }
32388     },
32389
32390     fireEvent : function(){
32391         return this.node.fireEvent.apply(this.node, arguments);
32392     },
32393
32394     initEvents : function(){
32395         this.node.on("move", this.onMove, this);
32396         var E = Roo.EventManager;
32397         var a = this.anchor;
32398
32399         var el = Roo.fly(a, '_treeui');
32400
32401         if(Roo.isOpera){ // opera render bug ignores the CSS
32402             el.setStyle("text-decoration", "none");
32403         }
32404
32405         el.on("click", this.onClick, this);
32406         el.on("dblclick", this.onDblClick, this);
32407
32408         if(this.checkbox){
32409             Roo.EventManager.on(this.checkbox,
32410                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32411         }
32412
32413         el.on("contextmenu", this.onContextMenu, this);
32414
32415         var icon = Roo.fly(this.iconNode);
32416         icon.on("click", this.onClick, this);
32417         icon.on("dblclick", this.onDblClick, this);
32418         icon.on("contextmenu", this.onContextMenu, this);
32419         E.on(this.ecNode, "click", this.ecClick, this, true);
32420
32421         if(this.node.disabled){
32422             this.addClass("x-tree-node-disabled");
32423         }
32424         if(this.node.hidden){
32425             this.addClass("x-tree-node-disabled");
32426         }
32427         var ot = this.node.getOwnerTree();
32428         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32429         if(dd && (!this.node.isRoot || ot.rootVisible)){
32430             Roo.dd.Registry.register(this.elNode, {
32431                 node: this.node,
32432                 handles: this.getDDHandles(),
32433                 isHandle: false
32434             });
32435         }
32436     },
32437
32438     getDDHandles : function(){
32439         return [this.iconNode, this.textNode];
32440     },
32441
32442     hide : function(){
32443         if(this.rendered){
32444             this.wrap.style.display = "none";
32445         }
32446     },
32447
32448     show : function(){
32449         if(this.rendered){
32450             this.wrap.style.display = "";
32451         }
32452     },
32453
32454     onContextMenu : function(e){
32455         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32456             e.preventDefault();
32457             this.focus();
32458             this.fireEvent("contextmenu", this.node, e);
32459         }
32460     },
32461
32462     onClick : function(e){
32463         if(this.dropping){
32464             e.stopEvent();
32465             return;
32466         }
32467         if(this.fireEvent("beforeclick", this.node, e) !== false){
32468             if(!this.disabled && this.node.attributes.href){
32469                 this.fireEvent("click", this.node, e);
32470                 return;
32471             }
32472             e.preventDefault();
32473             if(this.disabled){
32474                 return;
32475             }
32476
32477             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32478                 this.node.toggle();
32479             }
32480
32481             this.fireEvent("click", this.node, e);
32482         }else{
32483             e.stopEvent();
32484         }
32485     },
32486
32487     onDblClick : function(e){
32488         e.preventDefault();
32489         if(this.disabled){
32490             return;
32491         }
32492         if(this.checkbox){
32493             this.toggleCheck();
32494         }
32495         if(!this.animating && this.node.hasChildNodes()){
32496             this.node.toggle();
32497         }
32498         this.fireEvent("dblclick", this.node, e);
32499     },
32500
32501     onCheckChange : function(){
32502         var checked = this.checkbox.checked;
32503         this.node.attributes.checked = checked;
32504         this.fireEvent('checkchange', this.node, checked);
32505     },
32506
32507     ecClick : function(e){
32508         if(!this.animating && this.node.hasChildNodes()){
32509             this.node.toggle();
32510         }
32511     },
32512
32513     startDrop : function(){
32514         this.dropping = true;
32515     },
32516
32517     // delayed drop so the click event doesn't get fired on a drop
32518     endDrop : function(){
32519        setTimeout(function(){
32520            this.dropping = false;
32521        }.createDelegate(this), 50);
32522     },
32523
32524     expand : function(){
32525         this.updateExpandIcon();
32526         this.ctNode.style.display = "";
32527     },
32528
32529     focus : function(){
32530         if(!this.node.preventHScroll){
32531             try{this.anchor.focus();
32532             }catch(e){}
32533         }else if(!Roo.isIE){
32534             try{
32535                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32536                 var l = noscroll.scrollLeft;
32537                 this.anchor.focus();
32538                 noscroll.scrollLeft = l;
32539             }catch(e){}
32540         }
32541     },
32542
32543     toggleCheck : function(value){
32544         var cb = this.checkbox;
32545         if(cb){
32546             cb.checked = (value === undefined ? !cb.checked : value);
32547         }
32548     },
32549
32550     blur : function(){
32551         try{
32552             this.anchor.blur();
32553         }catch(e){}
32554     },
32555
32556     animExpand : function(callback){
32557         var ct = Roo.get(this.ctNode);
32558         ct.stopFx();
32559         if(!this.node.hasChildNodes()){
32560             this.updateExpandIcon();
32561             this.ctNode.style.display = "";
32562             Roo.callback(callback);
32563             return;
32564         }
32565         this.animating = true;
32566         this.updateExpandIcon();
32567
32568         ct.slideIn('t', {
32569            callback : function(){
32570                this.animating = false;
32571                Roo.callback(callback);
32572             },
32573             scope: this,
32574             duration: this.node.ownerTree.duration || .25
32575         });
32576     },
32577
32578     highlight : function(){
32579         var tree = this.node.getOwnerTree();
32580         Roo.fly(this.wrap).highlight(
32581             tree.hlColor || "C3DAF9",
32582             {endColor: tree.hlBaseColor}
32583         );
32584     },
32585
32586     collapse : function(){
32587         this.updateExpandIcon();
32588         this.ctNode.style.display = "none";
32589     },
32590
32591     animCollapse : function(callback){
32592         var ct = Roo.get(this.ctNode);
32593         ct.enableDisplayMode('block');
32594         ct.stopFx();
32595
32596         this.animating = true;
32597         this.updateExpandIcon();
32598
32599         ct.slideOut('t', {
32600             callback : function(){
32601                this.animating = false;
32602                Roo.callback(callback);
32603             },
32604             scope: this,
32605             duration: this.node.ownerTree.duration || .25
32606         });
32607     },
32608
32609     getContainer : function(){
32610         return this.ctNode;
32611     },
32612
32613     getEl : function(){
32614         return this.wrap;
32615     },
32616
32617     appendDDGhost : function(ghostNode){
32618         ghostNode.appendChild(this.elNode.cloneNode(true));
32619     },
32620
32621     getDDRepairXY : function(){
32622         return Roo.lib.Dom.getXY(this.iconNode);
32623     },
32624
32625     onRender : function(){
32626         this.render();
32627     },
32628
32629     render : function(bulkRender){
32630         var n = this.node, a = n.attributes;
32631         var targetNode = n.parentNode ?
32632               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32633
32634         if(!this.rendered){
32635             this.rendered = true;
32636
32637             this.renderElements(n, a, targetNode, bulkRender);
32638
32639             if(a.qtip){
32640                if(this.textNode.setAttributeNS){
32641                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32642                    if(a.qtipTitle){
32643                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32644                    }
32645                }else{
32646                    this.textNode.setAttribute("ext:qtip", a.qtip);
32647                    if(a.qtipTitle){
32648                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32649                    }
32650                }
32651             }else if(a.qtipCfg){
32652                 a.qtipCfg.target = Roo.id(this.textNode);
32653                 Roo.QuickTips.register(a.qtipCfg);
32654             }
32655             this.initEvents();
32656             if(!this.node.expanded){
32657                 this.updateExpandIcon();
32658             }
32659         }else{
32660             if(bulkRender === true) {
32661                 targetNode.appendChild(this.wrap);
32662             }
32663         }
32664     },
32665
32666     renderElements : function(n, a, targetNode, bulkRender)
32667     {
32668         // add some indent caching, this helps performance when rendering a large tree
32669         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32670         var t = n.getOwnerTree();
32671         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32672         if (typeof(n.attributes.html) != 'undefined') {
32673             txt = n.attributes.html;
32674         }
32675         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32676         var cb = typeof a.checked == 'boolean';
32677         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32678         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32679             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32680             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32681             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32682             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32683             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32684              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32685                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32686             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32687             "</li>"];
32688
32689         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32690             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32691                                 n.nextSibling.ui.getEl(), buf.join(""));
32692         }else{
32693             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32694         }
32695
32696         this.elNode = this.wrap.childNodes[0];
32697         this.ctNode = this.wrap.childNodes[1];
32698         var cs = this.elNode.childNodes;
32699         this.indentNode = cs[0];
32700         this.ecNode = cs[1];
32701         this.iconNode = cs[2];
32702         var index = 3;
32703         if(cb){
32704             this.checkbox = cs[3];
32705             index++;
32706         }
32707         this.anchor = cs[index];
32708         this.textNode = cs[index].firstChild;
32709     },
32710
32711     getAnchor : function(){
32712         return this.anchor;
32713     },
32714
32715     getTextEl : function(){
32716         return this.textNode;
32717     },
32718
32719     getIconEl : function(){
32720         return this.iconNode;
32721     },
32722
32723     isChecked : function(){
32724         return this.checkbox ? this.checkbox.checked : false;
32725     },
32726
32727     updateExpandIcon : function(){
32728         if(this.rendered){
32729             var n = this.node, c1, c2;
32730             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32731             var hasChild = n.hasChildNodes();
32732             if(hasChild){
32733                 if(n.expanded){
32734                     cls += "-minus";
32735                     c1 = "x-tree-node-collapsed";
32736                     c2 = "x-tree-node-expanded";
32737                 }else{
32738                     cls += "-plus";
32739                     c1 = "x-tree-node-expanded";
32740                     c2 = "x-tree-node-collapsed";
32741                 }
32742                 if(this.wasLeaf){
32743                     this.removeClass("x-tree-node-leaf");
32744                     this.wasLeaf = false;
32745                 }
32746                 if(this.c1 != c1 || this.c2 != c2){
32747                     Roo.fly(this.elNode).replaceClass(c1, c2);
32748                     this.c1 = c1; this.c2 = c2;
32749                 }
32750             }else{
32751                 // this changes non-leafs into leafs if they have no children.
32752                 // it's not very rational behaviour..
32753                 
32754                 if(!this.wasLeaf && this.node.leaf){
32755                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32756                     delete this.c1;
32757                     delete this.c2;
32758                     this.wasLeaf = true;
32759                 }
32760             }
32761             var ecc = "x-tree-ec-icon "+cls;
32762             if(this.ecc != ecc){
32763                 this.ecNode.className = ecc;
32764                 this.ecc = ecc;
32765             }
32766         }
32767     },
32768
32769     getChildIndent : function(){
32770         if(!this.childIndent){
32771             var buf = [];
32772             var p = this.node;
32773             while(p){
32774                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32775                     if(!p.isLast()) {
32776                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32777                     } else {
32778                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32779                     }
32780                 }
32781                 p = p.parentNode;
32782             }
32783             this.childIndent = buf.join("");
32784         }
32785         return this.childIndent;
32786     },
32787
32788     renderIndent : function(){
32789         if(this.rendered){
32790             var indent = "";
32791             var p = this.node.parentNode;
32792             if(p){
32793                 indent = p.ui.getChildIndent();
32794             }
32795             if(this.indentMarkup != indent){ // don't rerender if not required
32796                 this.indentNode.innerHTML = indent;
32797                 this.indentMarkup = indent;
32798             }
32799             this.updateExpandIcon();
32800         }
32801     }
32802 };
32803
32804 Roo.tree.RootTreeNodeUI = function(){
32805     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32806 };
32807 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32808     render : function(){
32809         if(!this.rendered){
32810             var targetNode = this.node.ownerTree.innerCt.dom;
32811             this.node.expanded = true;
32812             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32813             this.wrap = this.ctNode = targetNode.firstChild;
32814         }
32815     },
32816     collapse : function(){
32817     },
32818     expand : function(){
32819     }
32820 });/*
32821  * Based on:
32822  * Ext JS Library 1.1.1
32823  * Copyright(c) 2006-2007, Ext JS, LLC.
32824  *
32825  * Originally Released Under LGPL - original licence link has changed is not relivant.
32826  *
32827  * Fork - LGPL
32828  * <script type="text/javascript">
32829  */
32830 /**
32831  * @class Roo.tree.TreeLoader
32832  * @extends Roo.util.Observable
32833  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32834  * nodes from a specified URL. The response must be a javascript Array definition
32835  * who's elements are node definition objects. eg:
32836  * <pre><code>
32837    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32838     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32839 </code></pre>
32840  * <br><br>
32841  * A server request is sent, and child nodes are loaded only when a node is expanded.
32842  * The loading node's id is passed to the server under the parameter name "node" to
32843  * enable the server to produce the correct child nodes.
32844  * <br><br>
32845  * To pass extra parameters, an event handler may be attached to the "beforeload"
32846  * event, and the parameters specified in the TreeLoader's baseParams property:
32847  * <pre><code>
32848     myTreeLoader.on("beforeload", function(treeLoader, node) {
32849         this.baseParams.category = node.attributes.category;
32850     }, this);
32851 </code></pre><
32852  * This would pass an HTTP parameter called "category" to the server containing
32853  * the value of the Node's "category" attribute.
32854  * @constructor
32855  * Creates a new Treeloader.
32856  * @param {Object} config A config object containing config properties.
32857  */
32858 Roo.tree.TreeLoader = function(config){
32859     this.baseParams = {};
32860     this.requestMethod = "POST";
32861     Roo.apply(this, config);
32862
32863     this.addEvents({
32864     
32865         /**
32866          * @event beforeload
32867          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32868          * @param {Object} This TreeLoader object.
32869          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32870          * @param {Object} callback The callback function specified in the {@link #load} call.
32871          */
32872         beforeload : true,
32873         /**
32874          * @event load
32875          * Fires when the node has been successfuly loaded.
32876          * @param {Object} This TreeLoader object.
32877          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32878          * @param {Object} response The response object containing the data from the server.
32879          */
32880         load : true,
32881         /**
32882          * @event loadexception
32883          * Fires if the network request failed.
32884          * @param {Object} This TreeLoader object.
32885          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32886          * @param {Object} response The response object containing the data from the server.
32887          */
32888         loadexception : true,
32889         /**
32890          * @event create
32891          * Fires before a node is created, enabling you to return custom Node types 
32892          * @param {Object} This TreeLoader object.
32893          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32894          */
32895         create : true
32896     });
32897
32898     Roo.tree.TreeLoader.superclass.constructor.call(this);
32899 };
32900
32901 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32902     /**
32903     * @cfg {String} dataUrl The URL from which to request a Json string which
32904     * specifies an array of node definition object representing the child nodes
32905     * to be loaded.
32906     */
32907     /**
32908     * @cfg {Object} baseParams (optional) An object containing properties which
32909     * specify HTTP parameters to be passed to each request for child nodes.
32910     */
32911     /**
32912     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32913     * created by this loader. If the attributes sent by the server have an attribute in this object,
32914     * they take priority.
32915     */
32916     /**
32917     * @cfg {Object} uiProviders (optional) An object containing properties which
32918     * 
32919     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32920     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32921     * <i>uiProvider</i> attribute of a returned child node is a string rather
32922     * than a reference to a TreeNodeUI implementation, this that string value
32923     * is used as a property name in the uiProviders object. You can define the provider named
32924     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32925     */
32926     uiProviders : {},
32927
32928     /**
32929     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32930     * child nodes before loading.
32931     */
32932     clearOnLoad : true,
32933
32934     /**
32935     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32936     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32937     * Grid query { data : [ .....] }
32938     */
32939     
32940     root : false,
32941      /**
32942     * @cfg {String} queryParam (optional) 
32943     * Name of the query as it will be passed on the querystring (defaults to 'node')
32944     * eg. the request will be ?node=[id]
32945     */
32946     
32947     
32948     queryParam: false,
32949     
32950     /**
32951      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32952      * This is called automatically when a node is expanded, but may be used to reload
32953      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32954      * @param {Roo.tree.TreeNode} node
32955      * @param {Function} callback
32956      */
32957     load : function(node, callback){
32958         if(this.clearOnLoad){
32959             while(node.firstChild){
32960                 node.removeChild(node.firstChild);
32961             }
32962         }
32963         if(node.attributes.children){ // preloaded json children
32964             var cs = node.attributes.children;
32965             for(var i = 0, len = cs.length; i < len; i++){
32966                 node.appendChild(this.createNode(cs[i]));
32967             }
32968             if(typeof callback == "function"){
32969                 callback();
32970             }
32971         }else if(this.dataUrl){
32972             this.requestData(node, callback);
32973         }
32974     },
32975
32976     getParams: function(node){
32977         var buf = [], bp = this.baseParams;
32978         for(var key in bp){
32979             if(typeof bp[key] != "function"){
32980                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32981             }
32982         }
32983         var n = this.queryParam === false ? 'node' : this.queryParam;
32984         buf.push(n + "=", encodeURIComponent(node.id));
32985         return buf.join("");
32986     },
32987
32988     requestData : function(node, callback){
32989         if(this.fireEvent("beforeload", this, node, callback) !== false){
32990             this.transId = Roo.Ajax.request({
32991                 method:this.requestMethod,
32992                 url: this.dataUrl||this.url,
32993                 success: this.handleResponse,
32994                 failure: this.handleFailure,
32995                 scope: this,
32996                 argument: {callback: callback, node: node},
32997                 params: this.getParams(node)
32998             });
32999         }else{
33000             // if the load is cancelled, make sure we notify
33001             // the node that we are done
33002             if(typeof callback == "function"){
33003                 callback();
33004             }
33005         }
33006     },
33007
33008     isLoading : function(){
33009         return this.transId ? true : false;
33010     },
33011
33012     abort : function(){
33013         if(this.isLoading()){
33014             Roo.Ajax.abort(this.transId);
33015         }
33016     },
33017
33018     // private
33019     createNode : function(attr)
33020     {
33021         // apply baseAttrs, nice idea Corey!
33022         if(this.baseAttrs){
33023             Roo.applyIf(attr, this.baseAttrs);
33024         }
33025         if(this.applyLoader !== false){
33026             attr.loader = this;
33027         }
33028         // uiProvider = depreciated..
33029         
33030         if(typeof(attr.uiProvider) == 'string'){
33031            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33032                 /**  eval:var:attr */ eval(attr.uiProvider);
33033         }
33034         if(typeof(this.uiProviders['default']) != 'undefined') {
33035             attr.uiProvider = this.uiProviders['default'];
33036         }
33037         
33038         this.fireEvent('create', this, attr);
33039         
33040         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33041         return(attr.leaf ?
33042                         new Roo.tree.TreeNode(attr) :
33043                         new Roo.tree.AsyncTreeNode(attr));
33044     },
33045
33046     processResponse : function(response, node, callback)
33047     {
33048         var json = response.responseText;
33049         try {
33050             
33051             var o = Roo.decode(json);
33052             
33053             if (!o.success) {
33054                 // it's a failure condition.
33055                 var a = response.argument;
33056                 this.fireEvent("loadexception", this, a.node, response);
33057                 Roo.log("Load failed - should have a handler really");
33058                 return;
33059             }
33060             
33061             if (this.root !== false) {
33062                 o = o[this.root];
33063             }
33064             
33065             for(var i = 0, len = o.length; i < len; i++){
33066                 var n = this.createNode(o[i]);
33067                 if(n){
33068                     node.appendChild(n);
33069                 }
33070             }
33071             if(typeof callback == "function"){
33072                 callback(this, node);
33073             }
33074         }catch(e){
33075             this.handleFailure(response);
33076         }
33077     },
33078
33079     handleResponse : function(response){
33080         this.transId = false;
33081         var a = response.argument;
33082         this.processResponse(response, a.node, a.callback);
33083         this.fireEvent("load", this, a.node, response);
33084     },
33085
33086     handleFailure : function(response)
33087     {
33088         // should handle failure better..
33089         this.transId = false;
33090         var a = response.argument;
33091         this.fireEvent("loadexception", this, a.node, response);
33092         if(typeof a.callback == "function"){
33093             a.callback(this, a.node);
33094         }
33095     }
33096 });/*
33097  * Based on:
33098  * Ext JS Library 1.1.1
33099  * Copyright(c) 2006-2007, Ext JS, LLC.
33100  *
33101  * Originally Released Under LGPL - original licence link has changed is not relivant.
33102  *
33103  * Fork - LGPL
33104  * <script type="text/javascript">
33105  */
33106
33107 /**
33108 * @class Roo.tree.TreeFilter
33109 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33110 * @param {TreePanel} tree
33111 * @param {Object} config (optional)
33112  */
33113 Roo.tree.TreeFilter = function(tree, config){
33114     this.tree = tree;
33115     this.filtered = {};
33116     Roo.apply(this, config);
33117 };
33118
33119 Roo.tree.TreeFilter.prototype = {
33120     clearBlank:false,
33121     reverse:false,
33122     autoClear:false,
33123     remove:false,
33124
33125      /**
33126      * Filter the data by a specific attribute.
33127      * @param {String/RegExp} value Either string that the attribute value
33128      * should start with or a RegExp to test against the attribute
33129      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33130      * @param {TreeNode} startNode (optional) The node to start the filter at.
33131      */
33132     filter : function(value, attr, startNode){
33133         attr = attr || "text";
33134         var f;
33135         if(typeof value == "string"){
33136             var vlen = value.length;
33137             // auto clear empty filter
33138             if(vlen == 0 && this.clearBlank){
33139                 this.clear();
33140                 return;
33141             }
33142             value = value.toLowerCase();
33143             f = function(n){
33144                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33145             };
33146         }else if(value.exec){ // regex?
33147             f = function(n){
33148                 return value.test(n.attributes[attr]);
33149             };
33150         }else{
33151             throw 'Illegal filter type, must be string or regex';
33152         }
33153         this.filterBy(f, null, startNode);
33154         },
33155
33156     /**
33157      * Filter by a function. The passed function will be called with each
33158      * node in the tree (or from the startNode). If the function returns true, the node is kept
33159      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33160      * @param {Function} fn The filter function
33161      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33162      */
33163     filterBy : function(fn, scope, startNode){
33164         startNode = startNode || this.tree.root;
33165         if(this.autoClear){
33166             this.clear();
33167         }
33168         var af = this.filtered, rv = this.reverse;
33169         var f = function(n){
33170             if(n == startNode){
33171                 return true;
33172             }
33173             if(af[n.id]){
33174                 return false;
33175             }
33176             var m = fn.call(scope || n, n);
33177             if(!m || rv){
33178                 af[n.id] = n;
33179                 n.ui.hide();
33180                 return false;
33181             }
33182             return true;
33183         };
33184         startNode.cascade(f);
33185         if(this.remove){
33186            for(var id in af){
33187                if(typeof id != "function"){
33188                    var n = af[id];
33189                    if(n && n.parentNode){
33190                        n.parentNode.removeChild(n);
33191                    }
33192                }
33193            }
33194         }
33195     },
33196
33197     /**
33198      * Clears the current filter. Note: with the "remove" option
33199      * set a filter cannot be cleared.
33200      */
33201     clear : function(){
33202         var t = this.tree;
33203         var af = this.filtered;
33204         for(var id in af){
33205             if(typeof id != "function"){
33206                 var n = af[id];
33207                 if(n){
33208                     n.ui.show();
33209                 }
33210             }
33211         }
33212         this.filtered = {};
33213     }
33214 };
33215 /*
33216  * Based on:
33217  * Ext JS Library 1.1.1
33218  * Copyright(c) 2006-2007, Ext JS, LLC.
33219  *
33220  * Originally Released Under LGPL - original licence link has changed is not relivant.
33221  *
33222  * Fork - LGPL
33223  * <script type="text/javascript">
33224  */
33225  
33226
33227 /**
33228  * @class Roo.tree.TreeSorter
33229  * Provides sorting of nodes in a TreePanel
33230  * 
33231  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33232  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33233  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33234  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33235  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33236  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33237  * @constructor
33238  * @param {TreePanel} tree
33239  * @param {Object} config
33240  */
33241 Roo.tree.TreeSorter = function(tree, config){
33242     Roo.apply(this, config);
33243     tree.on("beforechildrenrendered", this.doSort, this);
33244     tree.on("append", this.updateSort, this);
33245     tree.on("insert", this.updateSort, this);
33246     
33247     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33248     var p = this.property || "text";
33249     var sortType = this.sortType;
33250     var fs = this.folderSort;
33251     var cs = this.caseSensitive === true;
33252     var leafAttr = this.leafAttr || 'leaf';
33253
33254     this.sortFn = function(n1, n2){
33255         if(fs){
33256             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33257                 return 1;
33258             }
33259             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33260                 return -1;
33261             }
33262         }
33263         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33264         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33265         if(v1 < v2){
33266                         return dsc ? +1 : -1;
33267                 }else if(v1 > v2){
33268                         return dsc ? -1 : +1;
33269         }else{
33270                 return 0;
33271         }
33272     };
33273 };
33274
33275 Roo.tree.TreeSorter.prototype = {
33276     doSort : function(node){
33277         node.sort(this.sortFn);
33278     },
33279     
33280     compareNodes : function(n1, n2){
33281         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33282     },
33283     
33284     updateSort : function(tree, node){
33285         if(node.childrenRendered){
33286             this.doSort.defer(1, this, [node]);
33287         }
33288     }
33289 };/*
33290  * Based on:
33291  * Ext JS Library 1.1.1
33292  * Copyright(c) 2006-2007, Ext JS, LLC.
33293  *
33294  * Originally Released Under LGPL - original licence link has changed is not relivant.
33295  *
33296  * Fork - LGPL
33297  * <script type="text/javascript">
33298  */
33299
33300 if(Roo.dd.DropZone){
33301     
33302 Roo.tree.TreeDropZone = function(tree, config){
33303     this.allowParentInsert = false;
33304     this.allowContainerDrop = false;
33305     this.appendOnly = false;
33306     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33307     this.tree = tree;
33308     this.lastInsertClass = "x-tree-no-status";
33309     this.dragOverData = {};
33310 };
33311
33312 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33313     ddGroup : "TreeDD",
33314     
33315     expandDelay : 1000,
33316     
33317     expandNode : function(node){
33318         if(node.hasChildNodes() && !node.isExpanded()){
33319             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33320         }
33321     },
33322     
33323     queueExpand : function(node){
33324         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33325     },
33326     
33327     cancelExpand : function(){
33328         if(this.expandProcId){
33329             clearTimeout(this.expandProcId);
33330             this.expandProcId = false;
33331         }
33332     },
33333     
33334     isValidDropPoint : function(n, pt, dd, e, data){
33335         if(!n || !data){ return false; }
33336         var targetNode = n.node;
33337         var dropNode = data.node;
33338         // default drop rules
33339         if(!(targetNode && targetNode.isTarget && pt)){
33340             return false;
33341         }
33342         if(pt == "append" && targetNode.allowChildren === false){
33343             return false;
33344         }
33345         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33346             return false;
33347         }
33348         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33349             return false;
33350         }
33351         // reuse the object
33352         var overEvent = this.dragOverData;
33353         overEvent.tree = this.tree;
33354         overEvent.target = targetNode;
33355         overEvent.data = data;
33356         overEvent.point = pt;
33357         overEvent.source = dd;
33358         overEvent.rawEvent = e;
33359         overEvent.dropNode = dropNode;
33360         overEvent.cancel = false;  
33361         var result = this.tree.fireEvent("nodedragover", overEvent);
33362         return overEvent.cancel === false && result !== false;
33363     },
33364     
33365     getDropPoint : function(e, n, dd){
33366         var tn = n.node;
33367         if(tn.isRoot){
33368             return tn.allowChildren !== false ? "append" : false; // always append for root
33369         }
33370         var dragEl = n.ddel;
33371         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33372         var y = Roo.lib.Event.getPageY(e);
33373         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33374         
33375         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33376         var noAppend = tn.allowChildren === false;
33377         if(this.appendOnly || tn.parentNode.allowChildren === false){
33378             return noAppend ? false : "append";
33379         }
33380         var noBelow = false;
33381         if(!this.allowParentInsert){
33382             noBelow = tn.hasChildNodes() && tn.isExpanded();
33383         }
33384         var q = (b - t) / (noAppend ? 2 : 3);
33385         if(y >= t && y < (t + q)){
33386             return "above";
33387         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33388             return "below";
33389         }else{
33390             return "append";
33391         }
33392     },
33393     
33394     onNodeEnter : function(n, dd, e, data){
33395         this.cancelExpand();
33396     },
33397     
33398     onNodeOver : function(n, dd, e, data){
33399         var pt = this.getDropPoint(e, n, dd);
33400         var node = n.node;
33401         
33402         // auto node expand check
33403         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33404             this.queueExpand(node);
33405         }else if(pt != "append"){
33406             this.cancelExpand();
33407         }
33408         
33409         // set the insert point style on the target node
33410         var returnCls = this.dropNotAllowed;
33411         if(this.isValidDropPoint(n, pt, dd, e, data)){
33412            if(pt){
33413                var el = n.ddel;
33414                var cls;
33415                if(pt == "above"){
33416                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33417                    cls = "x-tree-drag-insert-above";
33418                }else if(pt == "below"){
33419                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33420                    cls = "x-tree-drag-insert-below";
33421                }else{
33422                    returnCls = "x-tree-drop-ok-append";
33423                    cls = "x-tree-drag-append";
33424                }
33425                if(this.lastInsertClass != cls){
33426                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33427                    this.lastInsertClass = cls;
33428                }
33429            }
33430        }
33431        return returnCls;
33432     },
33433     
33434     onNodeOut : function(n, dd, e, data){
33435         this.cancelExpand();
33436         this.removeDropIndicators(n);
33437     },
33438     
33439     onNodeDrop : function(n, dd, e, data){
33440         var point = this.getDropPoint(e, n, dd);
33441         var targetNode = n.node;
33442         targetNode.ui.startDrop();
33443         if(!this.isValidDropPoint(n, point, dd, e, data)){
33444             targetNode.ui.endDrop();
33445             return false;
33446         }
33447         // first try to find the drop node
33448         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33449         var dropEvent = {
33450             tree : this.tree,
33451             target: targetNode,
33452             data: data,
33453             point: point,
33454             source: dd,
33455             rawEvent: e,
33456             dropNode: dropNode,
33457             cancel: !dropNode   
33458         };
33459         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33460         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33461             targetNode.ui.endDrop();
33462             return false;
33463         }
33464         // allow target changing
33465         targetNode = dropEvent.target;
33466         if(point == "append" && !targetNode.isExpanded()){
33467             targetNode.expand(false, null, function(){
33468                 this.completeDrop(dropEvent);
33469             }.createDelegate(this));
33470         }else{
33471             this.completeDrop(dropEvent);
33472         }
33473         return true;
33474     },
33475     
33476     completeDrop : function(de){
33477         var ns = de.dropNode, p = de.point, t = de.target;
33478         if(!(ns instanceof Array)){
33479             ns = [ns];
33480         }
33481         var n;
33482         for(var i = 0, len = ns.length; i < len; i++){
33483             n = ns[i];
33484             if(p == "above"){
33485                 t.parentNode.insertBefore(n, t);
33486             }else if(p == "below"){
33487                 t.parentNode.insertBefore(n, t.nextSibling);
33488             }else{
33489                 t.appendChild(n);
33490             }
33491         }
33492         n.ui.focus();
33493         if(this.tree.hlDrop){
33494             n.ui.highlight();
33495         }
33496         t.ui.endDrop();
33497         this.tree.fireEvent("nodedrop", de);
33498     },
33499     
33500     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33501         if(this.tree.hlDrop){
33502             dropNode.ui.focus();
33503             dropNode.ui.highlight();
33504         }
33505         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33506     },
33507     
33508     getTree : function(){
33509         return this.tree;
33510     },
33511     
33512     removeDropIndicators : function(n){
33513         if(n && n.ddel){
33514             var el = n.ddel;
33515             Roo.fly(el).removeClass([
33516                     "x-tree-drag-insert-above",
33517                     "x-tree-drag-insert-below",
33518                     "x-tree-drag-append"]);
33519             this.lastInsertClass = "_noclass";
33520         }
33521     },
33522     
33523     beforeDragDrop : function(target, e, id){
33524         this.cancelExpand();
33525         return true;
33526     },
33527     
33528     afterRepair : function(data){
33529         if(data && Roo.enableFx){
33530             data.node.ui.highlight();
33531         }
33532         this.hideProxy();
33533     }    
33534 });
33535
33536 }
33537 /*
33538  * Based on:
33539  * Ext JS Library 1.1.1
33540  * Copyright(c) 2006-2007, Ext JS, LLC.
33541  *
33542  * Originally Released Under LGPL - original licence link has changed is not relivant.
33543  *
33544  * Fork - LGPL
33545  * <script type="text/javascript">
33546  */
33547  
33548
33549 if(Roo.dd.DragZone){
33550 Roo.tree.TreeDragZone = function(tree, config){
33551     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33552     this.tree = tree;
33553 };
33554
33555 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33556     ddGroup : "TreeDD",
33557     
33558     onBeforeDrag : function(data, e){
33559         var n = data.node;
33560         return n && n.draggable && !n.disabled;
33561     },
33562     
33563     onInitDrag : function(e){
33564         var data = this.dragData;
33565         this.tree.getSelectionModel().select(data.node);
33566         this.proxy.update("");
33567         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33568         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33569     },
33570     
33571     getRepairXY : function(e, data){
33572         return data.node.ui.getDDRepairXY();
33573     },
33574     
33575     onEndDrag : function(data, e){
33576         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33577     },
33578     
33579     onValidDrop : function(dd, e, id){
33580         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33581         this.hideProxy();
33582     },
33583     
33584     beforeInvalidDrop : function(e, id){
33585         // this scrolls the original position back into view
33586         var sm = this.tree.getSelectionModel();
33587         sm.clearSelections();
33588         sm.select(this.dragData.node);
33589     }
33590 });
33591 }/*
33592  * Based on:
33593  * Ext JS Library 1.1.1
33594  * Copyright(c) 2006-2007, Ext JS, LLC.
33595  *
33596  * Originally Released Under LGPL - original licence link has changed is not relivant.
33597  *
33598  * Fork - LGPL
33599  * <script type="text/javascript">
33600  */
33601 /**
33602  * @class Roo.tree.TreeEditor
33603  * @extends Roo.Editor
33604  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33605  * as the editor field.
33606  * @constructor
33607  * @param {Object} config (used to be the tree panel.)
33608  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33609  * 
33610  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33611  * @cfg {Roo.form.TextField|Object} field The field configuration
33612  *
33613  * 
33614  */
33615 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33616     var tree = config;
33617     var field;
33618     if (oldconfig) { // old style..
33619         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33620     } else {
33621         // new style..
33622         tree = config.tree;
33623         config.field = config.field  || {};
33624         config.field.xtype = 'TextField';
33625         field = Roo.factory(config.field, Roo.form);
33626     }
33627     config = config || {};
33628     
33629     
33630     this.addEvents({
33631         /**
33632          * @event beforenodeedit
33633          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33634          * false from the handler of this event.
33635          * @param {Editor} this
33636          * @param {Roo.tree.Node} node 
33637          */
33638         "beforenodeedit" : true
33639     });
33640     
33641     //Roo.log(config);
33642     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33643
33644     this.tree = tree;
33645
33646     tree.on('beforeclick', this.beforeNodeClick, this);
33647     tree.getTreeEl().on('mousedown', this.hide, this);
33648     this.on('complete', this.updateNode, this);
33649     this.on('beforestartedit', this.fitToTree, this);
33650     this.on('startedit', this.bindScroll, this, {delay:10});
33651     this.on('specialkey', this.onSpecialKey, this);
33652 };
33653
33654 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33655     /**
33656      * @cfg {String} alignment
33657      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33658      */
33659     alignment: "l-l",
33660     // inherit
33661     autoSize: false,
33662     /**
33663      * @cfg {Boolean} hideEl
33664      * True to hide the bound element while the editor is displayed (defaults to false)
33665      */
33666     hideEl : false,
33667     /**
33668      * @cfg {String} cls
33669      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33670      */
33671     cls: "x-small-editor x-tree-editor",
33672     /**
33673      * @cfg {Boolean} shim
33674      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33675      */
33676     shim:false,
33677     // inherit
33678     shadow:"frame",
33679     /**
33680      * @cfg {Number} maxWidth
33681      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33682      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33683      * scroll and client offsets into account prior to each edit.
33684      */
33685     maxWidth: 250,
33686
33687     editDelay : 350,
33688
33689     // private
33690     fitToTree : function(ed, el){
33691         var td = this.tree.getTreeEl().dom, nd = el.dom;
33692         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33693             td.scrollLeft = nd.offsetLeft;
33694         }
33695         var w = Math.min(
33696                 this.maxWidth,
33697                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33698         this.setSize(w, '');
33699         
33700         return this.fireEvent('beforenodeedit', this, this.editNode);
33701         
33702     },
33703
33704     // private
33705     triggerEdit : function(node){
33706         this.completeEdit();
33707         this.editNode = node;
33708         this.startEdit(node.ui.textNode, node.text);
33709     },
33710
33711     // private
33712     bindScroll : function(){
33713         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33714     },
33715
33716     // private
33717     beforeNodeClick : function(node, e){
33718         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33719         this.lastClick = new Date();
33720         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33721             e.stopEvent();
33722             this.triggerEdit(node);
33723             return false;
33724         }
33725         return true;
33726     },
33727
33728     // private
33729     updateNode : function(ed, value){
33730         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33731         this.editNode.setText(value);
33732     },
33733
33734     // private
33735     onHide : function(){
33736         Roo.tree.TreeEditor.superclass.onHide.call(this);
33737         if(this.editNode){
33738             this.editNode.ui.focus();
33739         }
33740     },
33741
33742     // private
33743     onSpecialKey : function(field, e){
33744         var k = e.getKey();
33745         if(k == e.ESC){
33746             e.stopEvent();
33747             this.cancelEdit();
33748         }else if(k == e.ENTER && !e.hasModifier()){
33749             e.stopEvent();
33750             this.completeEdit();
33751         }
33752     }
33753 });//<Script type="text/javascript">
33754 /*
33755  * Based on:
33756  * Ext JS Library 1.1.1
33757  * Copyright(c) 2006-2007, Ext JS, LLC.
33758  *
33759  * Originally Released Under LGPL - original licence link has changed is not relivant.
33760  *
33761  * Fork - LGPL
33762  * <script type="text/javascript">
33763  */
33764  
33765 /**
33766  * Not documented??? - probably should be...
33767  */
33768
33769 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33770     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33771     
33772     renderElements : function(n, a, targetNode, bulkRender){
33773         //consel.log("renderElements?");
33774         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33775
33776         var t = n.getOwnerTree();
33777         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33778         
33779         var cols = t.columns;
33780         var bw = t.borderWidth;
33781         var c = cols[0];
33782         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33783          var cb = typeof a.checked == "boolean";
33784         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33785         var colcls = 'x-t-' + tid + '-c0';
33786         var buf = [
33787             '<li class="x-tree-node">',
33788             
33789                 
33790                 '<div class="x-tree-node-el ', a.cls,'">',
33791                     // extran...
33792                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33793                 
33794                 
33795                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33796                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33797                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33798                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33799                            (a.iconCls ? ' '+a.iconCls : ''),
33800                            '" unselectable="on" />',
33801                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33802                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33803                              
33804                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33805                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33806                             '<span unselectable="on" qtip="' + tx + '">',
33807                              tx,
33808                              '</span></a>' ,
33809                     '</div>',
33810                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33811                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33812                  ];
33813         for(var i = 1, len = cols.length; i < len; i++){
33814             c = cols[i];
33815             colcls = 'x-t-' + tid + '-c' +i;
33816             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33817             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33818                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33819                       "</div>");
33820          }
33821          
33822          buf.push(
33823             '</a>',
33824             '<div class="x-clear"></div></div>',
33825             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33826             "</li>");
33827         
33828         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33829             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33830                                 n.nextSibling.ui.getEl(), buf.join(""));
33831         }else{
33832             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33833         }
33834         var el = this.wrap.firstChild;
33835         this.elRow = el;
33836         this.elNode = el.firstChild;
33837         this.ranchor = el.childNodes[1];
33838         this.ctNode = this.wrap.childNodes[1];
33839         var cs = el.firstChild.childNodes;
33840         this.indentNode = cs[0];
33841         this.ecNode = cs[1];
33842         this.iconNode = cs[2];
33843         var index = 3;
33844         if(cb){
33845             this.checkbox = cs[3];
33846             index++;
33847         }
33848         this.anchor = cs[index];
33849         
33850         this.textNode = cs[index].firstChild;
33851         
33852         //el.on("click", this.onClick, this);
33853         //el.on("dblclick", this.onDblClick, this);
33854         
33855         
33856        // console.log(this);
33857     },
33858     initEvents : function(){
33859         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33860         
33861             
33862         var a = this.ranchor;
33863
33864         var el = Roo.get(a);
33865
33866         if(Roo.isOpera){ // opera render bug ignores the CSS
33867             el.setStyle("text-decoration", "none");
33868         }
33869
33870         el.on("click", this.onClick, this);
33871         el.on("dblclick", this.onDblClick, this);
33872         el.on("contextmenu", this.onContextMenu, this);
33873         
33874     },
33875     
33876     /*onSelectedChange : function(state){
33877         if(state){
33878             this.focus();
33879             this.addClass("x-tree-selected");
33880         }else{
33881             //this.blur();
33882             this.removeClass("x-tree-selected");
33883         }
33884     },*/
33885     addClass : function(cls){
33886         if(this.elRow){
33887             Roo.fly(this.elRow).addClass(cls);
33888         }
33889         
33890     },
33891     
33892     
33893     removeClass : function(cls){
33894         if(this.elRow){
33895             Roo.fly(this.elRow).removeClass(cls);
33896         }
33897     }
33898
33899     
33900     
33901 });//<Script type="text/javascript">
33902
33903 /*
33904  * Based on:
33905  * Ext JS Library 1.1.1
33906  * Copyright(c) 2006-2007, Ext JS, LLC.
33907  *
33908  * Originally Released Under LGPL - original licence link has changed is not relivant.
33909  *
33910  * Fork - LGPL
33911  * <script type="text/javascript">
33912  */
33913  
33914
33915 /**
33916  * @class Roo.tree.ColumnTree
33917  * @extends Roo.data.TreePanel
33918  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33919  * @cfg {int} borderWidth  compined right/left border allowance
33920  * @constructor
33921  * @param {String/HTMLElement/Element} el The container element
33922  * @param {Object} config
33923  */
33924 Roo.tree.ColumnTree =  function(el, config)
33925 {
33926    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33927    this.addEvents({
33928         /**
33929         * @event resize
33930         * Fire this event on a container when it resizes
33931         * @param {int} w Width
33932         * @param {int} h Height
33933         */
33934        "resize" : true
33935     });
33936     this.on('resize', this.onResize, this);
33937 };
33938
33939 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33940     //lines:false,
33941     
33942     
33943     borderWidth: Roo.isBorderBox ? 0 : 2, 
33944     headEls : false,
33945     
33946     render : function(){
33947         // add the header.....
33948        
33949         Roo.tree.ColumnTree.superclass.render.apply(this);
33950         
33951         this.el.addClass('x-column-tree');
33952         
33953         this.headers = this.el.createChild(
33954             {cls:'x-tree-headers'},this.innerCt.dom);
33955    
33956         var cols = this.columns, c;
33957         var totalWidth = 0;
33958         this.headEls = [];
33959         var  len = cols.length;
33960         for(var i = 0; i < len; i++){
33961              c = cols[i];
33962              totalWidth += c.width;
33963             this.headEls.push(this.headers.createChild({
33964                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33965                  cn: {
33966                      cls:'x-tree-hd-text',
33967                      html: c.header
33968                  },
33969                  style:'width:'+(c.width-this.borderWidth)+'px;'
33970              }));
33971         }
33972         this.headers.createChild({cls:'x-clear'});
33973         // prevent floats from wrapping when clipped
33974         this.headers.setWidth(totalWidth);
33975         //this.innerCt.setWidth(totalWidth);
33976         this.innerCt.setStyle({ overflow: 'auto' });
33977         this.onResize(this.width, this.height);
33978              
33979         
33980     },
33981     onResize : function(w,h)
33982     {
33983         this.height = h;
33984         this.width = w;
33985         // resize cols..
33986         this.innerCt.setWidth(this.width);
33987         this.innerCt.setHeight(this.height-20);
33988         
33989         // headers...
33990         var cols = this.columns, c;
33991         var totalWidth = 0;
33992         var expEl = false;
33993         var len = cols.length;
33994         for(var i = 0; i < len; i++){
33995             c = cols[i];
33996             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33997                 // it's the expander..
33998                 expEl  = this.headEls[i];
33999                 continue;
34000             }
34001             totalWidth += c.width;
34002             
34003         }
34004         if (expEl) {
34005             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34006         }
34007         this.headers.setWidth(w-20);
34008
34009         
34010         
34011         
34012     }
34013 });
34014 /*
34015  * Based on:
34016  * Ext JS Library 1.1.1
34017  * Copyright(c) 2006-2007, Ext JS, LLC.
34018  *
34019  * Originally Released Under LGPL - original licence link has changed is not relivant.
34020  *
34021  * Fork - LGPL
34022  * <script type="text/javascript">
34023  */
34024  
34025 /**
34026  * @class Roo.menu.Menu
34027  * @extends Roo.util.Observable
34028  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34029  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34030  * @constructor
34031  * Creates a new Menu
34032  * @param {Object} config Configuration options
34033  */
34034 Roo.menu.Menu = function(config){
34035     Roo.apply(this, config);
34036     this.id = this.id || Roo.id();
34037     this.addEvents({
34038         /**
34039          * @event beforeshow
34040          * Fires before this menu is displayed
34041          * @param {Roo.menu.Menu} this
34042          */
34043         beforeshow : true,
34044         /**
34045          * @event beforehide
34046          * Fires before this menu is hidden
34047          * @param {Roo.menu.Menu} this
34048          */
34049         beforehide : true,
34050         /**
34051          * @event show
34052          * Fires after this menu is displayed
34053          * @param {Roo.menu.Menu} this
34054          */
34055         show : true,
34056         /**
34057          * @event hide
34058          * Fires after this menu is hidden
34059          * @param {Roo.menu.Menu} this
34060          */
34061         hide : true,
34062         /**
34063          * @event click
34064          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34065          * @param {Roo.menu.Menu} this
34066          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34067          * @param {Roo.EventObject} e
34068          */
34069         click : true,
34070         /**
34071          * @event mouseover
34072          * Fires when the mouse is hovering over this menu
34073          * @param {Roo.menu.Menu} this
34074          * @param {Roo.EventObject} e
34075          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34076          */
34077         mouseover : true,
34078         /**
34079          * @event mouseout
34080          * Fires when the mouse exits this menu
34081          * @param {Roo.menu.Menu} this
34082          * @param {Roo.EventObject} e
34083          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34084          */
34085         mouseout : true,
34086         /**
34087          * @event itemclick
34088          * Fires when a menu item contained in this menu is clicked
34089          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34090          * @param {Roo.EventObject} e
34091          */
34092         itemclick: true
34093     });
34094     if (this.registerMenu) {
34095         Roo.menu.MenuMgr.register(this);
34096     }
34097     
34098     var mis = this.items;
34099     this.items = new Roo.util.MixedCollection();
34100     if(mis){
34101         this.add.apply(this, mis);
34102     }
34103 };
34104
34105 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34106     /**
34107      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34108      */
34109     minWidth : 120,
34110     /**
34111      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34112      * for bottom-right shadow (defaults to "sides")
34113      */
34114     shadow : "sides",
34115     /**
34116      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34117      * this menu (defaults to "tl-tr?")
34118      */
34119     subMenuAlign : "tl-tr?",
34120     /**
34121      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34122      * relative to its element of origin (defaults to "tl-bl?")
34123      */
34124     defaultAlign : "tl-bl?",
34125     /**
34126      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34127      */
34128     allowOtherMenus : false,
34129     /**
34130      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34131      */
34132     registerMenu : true,
34133
34134     hidden:true,
34135
34136     // private
34137     render : function(){
34138         if(this.el){
34139             return;
34140         }
34141         var el = this.el = new Roo.Layer({
34142             cls: "x-menu",
34143             shadow:this.shadow,
34144             constrain: false,
34145             parentEl: this.parentEl || document.body,
34146             zindex:15000
34147         });
34148
34149         this.keyNav = new Roo.menu.MenuNav(this);
34150
34151         if(this.plain){
34152             el.addClass("x-menu-plain");
34153         }
34154         if(this.cls){
34155             el.addClass(this.cls);
34156         }
34157         // generic focus element
34158         this.focusEl = el.createChild({
34159             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34160         });
34161         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34162         ul.on("click", this.onClick, this);
34163         ul.on("mouseover", this.onMouseOver, this);
34164         ul.on("mouseout", this.onMouseOut, this);
34165         this.items.each(function(item){
34166             var li = document.createElement("li");
34167             li.className = "x-menu-list-item";
34168             ul.dom.appendChild(li);
34169             item.render(li, this);
34170         }, this);
34171         this.ul = ul;
34172         this.autoWidth();
34173     },
34174
34175     // private
34176     autoWidth : function(){
34177         var el = this.el, ul = this.ul;
34178         if(!el){
34179             return;
34180         }
34181         var w = this.width;
34182         if(w){
34183             el.setWidth(w);
34184         }else if(Roo.isIE){
34185             el.setWidth(this.minWidth);
34186             var t = el.dom.offsetWidth; // force recalc
34187             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34188         }
34189     },
34190
34191     // private
34192     delayAutoWidth : function(){
34193         if(this.rendered){
34194             if(!this.awTask){
34195                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34196             }
34197             this.awTask.delay(20);
34198         }
34199     },
34200
34201     // private
34202     findTargetItem : function(e){
34203         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34204         if(t && t.menuItemId){
34205             return this.items.get(t.menuItemId);
34206         }
34207     },
34208
34209     // private
34210     onClick : function(e){
34211         var t;
34212         if(t = this.findTargetItem(e)){
34213             t.onClick(e);
34214             this.fireEvent("click", this, t, e);
34215         }
34216     },
34217
34218     // private
34219     setActiveItem : function(item, autoExpand){
34220         if(item != this.activeItem){
34221             if(this.activeItem){
34222                 this.activeItem.deactivate();
34223             }
34224             this.activeItem = item;
34225             item.activate(autoExpand);
34226         }else if(autoExpand){
34227             item.expandMenu();
34228         }
34229     },
34230
34231     // private
34232     tryActivate : function(start, step){
34233         var items = this.items;
34234         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34235             var item = items.get(i);
34236             if(!item.disabled && item.canActivate){
34237                 this.setActiveItem(item, false);
34238                 return item;
34239             }
34240         }
34241         return false;
34242     },
34243
34244     // private
34245     onMouseOver : function(e){
34246         var t;
34247         if(t = this.findTargetItem(e)){
34248             if(t.canActivate && !t.disabled){
34249                 this.setActiveItem(t, true);
34250             }
34251         }
34252         this.fireEvent("mouseover", this, e, t);
34253     },
34254
34255     // private
34256     onMouseOut : function(e){
34257         var t;
34258         if(t = this.findTargetItem(e)){
34259             if(t == this.activeItem && t.shouldDeactivate(e)){
34260                 this.activeItem.deactivate();
34261                 delete this.activeItem;
34262             }
34263         }
34264         this.fireEvent("mouseout", this, e, t);
34265     },
34266
34267     /**
34268      * Read-only.  Returns true if the menu is currently displayed, else false.
34269      * @type Boolean
34270      */
34271     isVisible : function(){
34272         return this.el && !this.hidden;
34273     },
34274
34275     /**
34276      * Displays this menu relative to another element
34277      * @param {String/HTMLElement/Roo.Element} element The element to align to
34278      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34279      * the element (defaults to this.defaultAlign)
34280      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34281      */
34282     show : function(el, pos, parentMenu){
34283         this.parentMenu = parentMenu;
34284         if(!this.el){
34285             this.render();
34286         }
34287         this.fireEvent("beforeshow", this);
34288         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34289     },
34290
34291     /**
34292      * Displays this menu at a specific xy position
34293      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34294      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34295      */
34296     showAt : function(xy, parentMenu, /* private: */_e){
34297         this.parentMenu = parentMenu;
34298         if(!this.el){
34299             this.render();
34300         }
34301         if(_e !== false){
34302             this.fireEvent("beforeshow", this);
34303             xy = this.el.adjustForConstraints(xy);
34304         }
34305         this.el.setXY(xy);
34306         this.el.show();
34307         this.hidden = false;
34308         this.focus();
34309         this.fireEvent("show", this);
34310     },
34311
34312     focus : function(){
34313         if(!this.hidden){
34314             this.doFocus.defer(50, this);
34315         }
34316     },
34317
34318     doFocus : function(){
34319         if(!this.hidden){
34320             this.focusEl.focus();
34321         }
34322     },
34323
34324     /**
34325      * Hides this menu and optionally all parent menus
34326      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34327      */
34328     hide : function(deep){
34329         if(this.el && this.isVisible()){
34330             this.fireEvent("beforehide", this);
34331             if(this.activeItem){
34332                 this.activeItem.deactivate();
34333                 this.activeItem = null;
34334             }
34335             this.el.hide();
34336             this.hidden = true;
34337             this.fireEvent("hide", this);
34338         }
34339         if(deep === true && this.parentMenu){
34340             this.parentMenu.hide(true);
34341         }
34342     },
34343
34344     /**
34345      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34346      * Any of the following are valid:
34347      * <ul>
34348      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34349      * <li>An HTMLElement object which will be converted to a menu item</li>
34350      * <li>A menu item config object that will be created as a new menu item</li>
34351      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34352      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34353      * </ul>
34354      * Usage:
34355      * <pre><code>
34356 // Create the menu
34357 var menu = new Roo.menu.Menu();
34358
34359 // Create a menu item to add by reference
34360 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34361
34362 // Add a bunch of items at once using different methods.
34363 // Only the last item added will be returned.
34364 var item = menu.add(
34365     menuItem,                // add existing item by ref
34366     'Dynamic Item',          // new TextItem
34367     '-',                     // new separator
34368     { text: 'Config Item' }  // new item by config
34369 );
34370 </code></pre>
34371      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34372      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34373      */
34374     add : function(){
34375         var a = arguments, l = a.length, item;
34376         for(var i = 0; i < l; i++){
34377             var el = a[i];
34378             if ((typeof(el) == "object") && el.xtype && el.xns) {
34379                 el = Roo.factory(el, Roo.menu);
34380             }
34381             
34382             if(el.render){ // some kind of Item
34383                 item = this.addItem(el);
34384             }else if(typeof el == "string"){ // string
34385                 if(el == "separator" || el == "-"){
34386                     item = this.addSeparator();
34387                 }else{
34388                     item = this.addText(el);
34389                 }
34390             }else if(el.tagName || el.el){ // element
34391                 item = this.addElement(el);
34392             }else if(typeof el == "object"){ // must be menu item config?
34393                 item = this.addMenuItem(el);
34394             }
34395         }
34396         return item;
34397     },
34398
34399     /**
34400      * Returns this menu's underlying {@link Roo.Element} object
34401      * @return {Roo.Element} The element
34402      */
34403     getEl : function(){
34404         if(!this.el){
34405             this.render();
34406         }
34407         return this.el;
34408     },
34409
34410     /**
34411      * Adds a separator bar to the menu
34412      * @return {Roo.menu.Item} The menu item that was added
34413      */
34414     addSeparator : function(){
34415         return this.addItem(new Roo.menu.Separator());
34416     },
34417
34418     /**
34419      * Adds an {@link Roo.Element} object to the menu
34420      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34421      * @return {Roo.menu.Item} The menu item that was added
34422      */
34423     addElement : function(el){
34424         return this.addItem(new Roo.menu.BaseItem(el));
34425     },
34426
34427     /**
34428      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34429      * @param {Roo.menu.Item} item The menu item to add
34430      * @return {Roo.menu.Item} The menu item that was added
34431      */
34432     addItem : function(item){
34433         this.items.add(item);
34434         if(this.ul){
34435             var li = document.createElement("li");
34436             li.className = "x-menu-list-item";
34437             this.ul.dom.appendChild(li);
34438             item.render(li, this);
34439             this.delayAutoWidth();
34440         }
34441         return item;
34442     },
34443
34444     /**
34445      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34446      * @param {Object} config A MenuItem config object
34447      * @return {Roo.menu.Item} The menu item that was added
34448      */
34449     addMenuItem : function(config){
34450         if(!(config instanceof Roo.menu.Item)){
34451             if(typeof config.checked == "boolean"){ // must be check menu item config?
34452                 config = new Roo.menu.CheckItem(config);
34453             }else{
34454                 config = new Roo.menu.Item(config);
34455             }
34456         }
34457         return this.addItem(config);
34458     },
34459
34460     /**
34461      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34462      * @param {String} text The text to display in the menu item
34463      * @return {Roo.menu.Item} The menu item that was added
34464      */
34465     addText : function(text){
34466         return this.addItem(new Roo.menu.TextItem({ text : text }));
34467     },
34468
34469     /**
34470      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34471      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34472      * @param {Roo.menu.Item} item The menu item to add
34473      * @return {Roo.menu.Item} The menu item that was added
34474      */
34475     insert : function(index, item){
34476         this.items.insert(index, item);
34477         if(this.ul){
34478             var li = document.createElement("li");
34479             li.className = "x-menu-list-item";
34480             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34481             item.render(li, this);
34482             this.delayAutoWidth();
34483         }
34484         return item;
34485     },
34486
34487     /**
34488      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34489      * @param {Roo.menu.Item} item The menu item to remove
34490      */
34491     remove : function(item){
34492         this.items.removeKey(item.id);
34493         item.destroy();
34494     },
34495
34496     /**
34497      * Removes and destroys all items in the menu
34498      */
34499     removeAll : function(){
34500         var f;
34501         while(f = this.items.first()){
34502             this.remove(f);
34503         }
34504     }
34505 });
34506
34507 // MenuNav is a private utility class used internally by the Menu
34508 Roo.menu.MenuNav = function(menu){
34509     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34510     this.scope = this.menu = menu;
34511 };
34512
34513 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34514     doRelay : function(e, h){
34515         var k = e.getKey();
34516         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34517             this.menu.tryActivate(0, 1);
34518             return false;
34519         }
34520         return h.call(this.scope || this, e, this.menu);
34521     },
34522
34523     up : function(e, m){
34524         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34525             m.tryActivate(m.items.length-1, -1);
34526         }
34527     },
34528
34529     down : function(e, m){
34530         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34531             m.tryActivate(0, 1);
34532         }
34533     },
34534
34535     right : function(e, m){
34536         if(m.activeItem){
34537             m.activeItem.expandMenu(true);
34538         }
34539     },
34540
34541     left : function(e, m){
34542         m.hide();
34543         if(m.parentMenu && m.parentMenu.activeItem){
34544             m.parentMenu.activeItem.activate();
34545         }
34546     },
34547
34548     enter : function(e, m){
34549         if(m.activeItem){
34550             e.stopPropagation();
34551             m.activeItem.onClick(e);
34552             m.fireEvent("click", this, m.activeItem);
34553             return true;
34554         }
34555     }
34556 });/*
34557  * Based on:
34558  * Ext JS Library 1.1.1
34559  * Copyright(c) 2006-2007, Ext JS, LLC.
34560  *
34561  * Originally Released Under LGPL - original licence link has changed is not relivant.
34562  *
34563  * Fork - LGPL
34564  * <script type="text/javascript">
34565  */
34566  
34567 /**
34568  * @class Roo.menu.MenuMgr
34569  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34570  * @singleton
34571  */
34572 Roo.menu.MenuMgr = function(){
34573    var menus, active, groups = {}, attached = false, lastShow = new Date();
34574
34575    // private - called when first menu is created
34576    function init(){
34577        menus = {};
34578        active = new Roo.util.MixedCollection();
34579        Roo.get(document).addKeyListener(27, function(){
34580            if(active.length > 0){
34581                hideAll();
34582            }
34583        });
34584    }
34585
34586    // private
34587    function hideAll(){
34588        if(active && active.length > 0){
34589            var c = active.clone();
34590            c.each(function(m){
34591                m.hide();
34592            });
34593        }
34594    }
34595
34596    // private
34597    function onHide(m){
34598        active.remove(m);
34599        if(active.length < 1){
34600            Roo.get(document).un("mousedown", onMouseDown);
34601            attached = false;
34602        }
34603    }
34604
34605    // private
34606    function onShow(m){
34607        var last = active.last();
34608        lastShow = new Date();
34609        active.add(m);
34610        if(!attached){
34611            Roo.get(document).on("mousedown", onMouseDown);
34612            attached = true;
34613        }
34614        if(m.parentMenu){
34615           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34616           m.parentMenu.activeChild = m;
34617        }else if(last && last.isVisible()){
34618           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34619        }
34620    }
34621
34622    // private
34623    function onBeforeHide(m){
34624        if(m.activeChild){
34625            m.activeChild.hide();
34626        }
34627        if(m.autoHideTimer){
34628            clearTimeout(m.autoHideTimer);
34629            delete m.autoHideTimer;
34630        }
34631    }
34632
34633    // private
34634    function onBeforeShow(m){
34635        var pm = m.parentMenu;
34636        if(!pm && !m.allowOtherMenus){
34637            hideAll();
34638        }else if(pm && pm.activeChild && active != m){
34639            pm.activeChild.hide();
34640        }
34641    }
34642
34643    // private
34644    function onMouseDown(e){
34645        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34646            hideAll();
34647        }
34648    }
34649
34650    // private
34651    function onBeforeCheck(mi, state){
34652        if(state){
34653            var g = groups[mi.group];
34654            for(var i = 0, l = g.length; i < l; i++){
34655                if(g[i] != mi){
34656                    g[i].setChecked(false);
34657                }
34658            }
34659        }
34660    }
34661
34662    return {
34663
34664        /**
34665         * Hides all menus that are currently visible
34666         */
34667        hideAll : function(){
34668             hideAll();  
34669        },
34670
34671        // private
34672        register : function(menu){
34673            if(!menus){
34674                init();
34675            }
34676            menus[menu.id] = menu;
34677            menu.on("beforehide", onBeforeHide);
34678            menu.on("hide", onHide);
34679            menu.on("beforeshow", onBeforeShow);
34680            menu.on("show", onShow);
34681            var g = menu.group;
34682            if(g && menu.events["checkchange"]){
34683                if(!groups[g]){
34684                    groups[g] = [];
34685                }
34686                groups[g].push(menu);
34687                menu.on("checkchange", onCheck);
34688            }
34689        },
34690
34691         /**
34692          * Returns a {@link Roo.menu.Menu} object
34693          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34694          * be used to generate and return a new Menu instance.
34695          */
34696        get : function(menu){
34697            if(typeof menu == "string"){ // menu id
34698                return menus[menu];
34699            }else if(menu.events){  // menu instance
34700                return menu;
34701            }else if(typeof menu.length == 'number'){ // array of menu items?
34702                return new Roo.menu.Menu({items:menu});
34703            }else{ // otherwise, must be a config
34704                return new Roo.menu.Menu(menu);
34705            }
34706        },
34707
34708        // private
34709        unregister : function(menu){
34710            delete menus[menu.id];
34711            menu.un("beforehide", onBeforeHide);
34712            menu.un("hide", onHide);
34713            menu.un("beforeshow", onBeforeShow);
34714            menu.un("show", onShow);
34715            var g = menu.group;
34716            if(g && menu.events["checkchange"]){
34717                groups[g].remove(menu);
34718                menu.un("checkchange", onCheck);
34719            }
34720        },
34721
34722        // private
34723        registerCheckable : function(menuItem){
34724            var g = menuItem.group;
34725            if(g){
34726                if(!groups[g]){
34727                    groups[g] = [];
34728                }
34729                groups[g].push(menuItem);
34730                menuItem.on("beforecheckchange", onBeforeCheck);
34731            }
34732        },
34733
34734        // private
34735        unregisterCheckable : function(menuItem){
34736            var g = menuItem.group;
34737            if(g){
34738                groups[g].remove(menuItem);
34739                menuItem.un("beforecheckchange", onBeforeCheck);
34740            }
34741        }
34742    };
34743 }();/*
34744  * Based on:
34745  * Ext JS Library 1.1.1
34746  * Copyright(c) 2006-2007, Ext JS, LLC.
34747  *
34748  * Originally Released Under LGPL - original licence link has changed is not relivant.
34749  *
34750  * Fork - LGPL
34751  * <script type="text/javascript">
34752  */
34753  
34754
34755 /**
34756  * @class Roo.menu.BaseItem
34757  * @extends Roo.Component
34758  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34759  * management and base configuration options shared by all menu components.
34760  * @constructor
34761  * Creates a new BaseItem
34762  * @param {Object} config Configuration options
34763  */
34764 Roo.menu.BaseItem = function(config){
34765     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34766
34767     this.addEvents({
34768         /**
34769          * @event click
34770          * Fires when this item is clicked
34771          * @param {Roo.menu.BaseItem} this
34772          * @param {Roo.EventObject} e
34773          */
34774         click: true,
34775         /**
34776          * @event activate
34777          * Fires when this item is activated
34778          * @param {Roo.menu.BaseItem} this
34779          */
34780         activate : true,
34781         /**
34782          * @event deactivate
34783          * Fires when this item is deactivated
34784          * @param {Roo.menu.BaseItem} this
34785          */
34786         deactivate : true
34787     });
34788
34789     if(this.handler){
34790         this.on("click", this.handler, this.scope, true);
34791     }
34792 };
34793
34794 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34795     /**
34796      * @cfg {Function} handler
34797      * A function that will handle the click event of this menu item (defaults to undefined)
34798      */
34799     /**
34800      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34801      */
34802     canActivate : false,
34803     /**
34804      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34805      */
34806     activeClass : "x-menu-item-active",
34807     /**
34808      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34809      */
34810     hideOnClick : true,
34811     /**
34812      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34813      */
34814     hideDelay : 100,
34815
34816     // private
34817     ctype: "Roo.menu.BaseItem",
34818
34819     // private
34820     actionMode : "container",
34821
34822     // private
34823     render : function(container, parentMenu){
34824         this.parentMenu = parentMenu;
34825         Roo.menu.BaseItem.superclass.render.call(this, container);
34826         this.container.menuItemId = this.id;
34827     },
34828
34829     // private
34830     onRender : function(container, position){
34831         this.el = Roo.get(this.el);
34832         container.dom.appendChild(this.el.dom);
34833     },
34834
34835     // private
34836     onClick : function(e){
34837         if(!this.disabled && this.fireEvent("click", this, e) !== false
34838                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34839             this.handleClick(e);
34840         }else{
34841             e.stopEvent();
34842         }
34843     },
34844
34845     // private
34846     activate : function(){
34847         if(this.disabled){
34848             return false;
34849         }
34850         var li = this.container;
34851         li.addClass(this.activeClass);
34852         this.region = li.getRegion().adjust(2, 2, -2, -2);
34853         this.fireEvent("activate", this);
34854         return true;
34855     },
34856
34857     // private
34858     deactivate : function(){
34859         this.container.removeClass(this.activeClass);
34860         this.fireEvent("deactivate", this);
34861     },
34862
34863     // private
34864     shouldDeactivate : function(e){
34865         return !this.region || !this.region.contains(e.getPoint());
34866     },
34867
34868     // private
34869     handleClick : function(e){
34870         if(this.hideOnClick){
34871             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34872         }
34873     },
34874
34875     // private
34876     expandMenu : function(autoActivate){
34877         // do nothing
34878     },
34879
34880     // private
34881     hideMenu : function(){
34882         // do nothing
34883     }
34884 });/*
34885  * Based on:
34886  * Ext JS Library 1.1.1
34887  * Copyright(c) 2006-2007, Ext JS, LLC.
34888  *
34889  * Originally Released Under LGPL - original licence link has changed is not relivant.
34890  *
34891  * Fork - LGPL
34892  * <script type="text/javascript">
34893  */
34894  
34895 /**
34896  * @class Roo.menu.Adapter
34897  * @extends Roo.menu.BaseItem
34898  * 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.
34899  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34900  * @constructor
34901  * Creates a new Adapter
34902  * @param {Object} config Configuration options
34903  */
34904 Roo.menu.Adapter = function(component, config){
34905     Roo.menu.Adapter.superclass.constructor.call(this, config);
34906     this.component = component;
34907 };
34908 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34909     // private
34910     canActivate : true,
34911
34912     // private
34913     onRender : function(container, position){
34914         this.component.render(container);
34915         this.el = this.component.getEl();
34916     },
34917
34918     // private
34919     activate : function(){
34920         if(this.disabled){
34921             return false;
34922         }
34923         this.component.focus();
34924         this.fireEvent("activate", this);
34925         return true;
34926     },
34927
34928     // private
34929     deactivate : function(){
34930         this.fireEvent("deactivate", this);
34931     },
34932
34933     // private
34934     disable : function(){
34935         this.component.disable();
34936         Roo.menu.Adapter.superclass.disable.call(this);
34937     },
34938
34939     // private
34940     enable : function(){
34941         this.component.enable();
34942         Roo.menu.Adapter.superclass.enable.call(this);
34943     }
34944 });/*
34945  * Based on:
34946  * Ext JS Library 1.1.1
34947  * Copyright(c) 2006-2007, Ext JS, LLC.
34948  *
34949  * Originally Released Under LGPL - original licence link has changed is not relivant.
34950  *
34951  * Fork - LGPL
34952  * <script type="text/javascript">
34953  */
34954
34955 /**
34956  * @class Roo.menu.TextItem
34957  * @extends Roo.menu.BaseItem
34958  * Adds a static text string to a menu, usually used as either a heading or group separator.
34959  * Note: old style constructor with text is still supported.
34960  * 
34961  * @constructor
34962  * Creates a new TextItem
34963  * @param {Object} cfg Configuration
34964  */
34965 Roo.menu.TextItem = function(cfg){
34966     if (typeof(cfg) == 'string') {
34967         this.text = cfg;
34968     } else {
34969         Roo.apply(this,cfg);
34970     }
34971     
34972     Roo.menu.TextItem.superclass.constructor.call(this);
34973 };
34974
34975 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34976     /**
34977      * @cfg {Boolean} text Text to show on item.
34978      */
34979     text : '',
34980     
34981     /**
34982      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34983      */
34984     hideOnClick : false,
34985     /**
34986      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34987      */
34988     itemCls : "x-menu-text",
34989
34990     // private
34991     onRender : function(){
34992         var s = document.createElement("span");
34993         s.className = this.itemCls;
34994         s.innerHTML = this.text;
34995         this.el = s;
34996         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34997     }
34998 });/*
34999  * Based on:
35000  * Ext JS Library 1.1.1
35001  * Copyright(c) 2006-2007, Ext JS, LLC.
35002  *
35003  * Originally Released Under LGPL - original licence link has changed is not relivant.
35004  *
35005  * Fork - LGPL
35006  * <script type="text/javascript">
35007  */
35008
35009 /**
35010  * @class Roo.menu.Separator
35011  * @extends Roo.menu.BaseItem
35012  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35013  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35014  * @constructor
35015  * @param {Object} config Configuration options
35016  */
35017 Roo.menu.Separator = function(config){
35018     Roo.menu.Separator.superclass.constructor.call(this, config);
35019 };
35020
35021 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35022     /**
35023      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35024      */
35025     itemCls : "x-menu-sep",
35026     /**
35027      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35028      */
35029     hideOnClick : false,
35030
35031     // private
35032     onRender : function(li){
35033         var s = document.createElement("span");
35034         s.className = this.itemCls;
35035         s.innerHTML = "&#160;";
35036         this.el = s;
35037         li.addClass("x-menu-sep-li");
35038         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35039     }
35040 });/*
35041  * Based on:
35042  * Ext JS Library 1.1.1
35043  * Copyright(c) 2006-2007, Ext JS, LLC.
35044  *
35045  * Originally Released Under LGPL - original licence link has changed is not relivant.
35046  *
35047  * Fork - LGPL
35048  * <script type="text/javascript">
35049  */
35050 /**
35051  * @class Roo.menu.Item
35052  * @extends Roo.menu.BaseItem
35053  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35054  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35055  * activation and click handling.
35056  * @constructor
35057  * Creates a new Item
35058  * @param {Object} config Configuration options
35059  */
35060 Roo.menu.Item = function(config){
35061     Roo.menu.Item.superclass.constructor.call(this, config);
35062     if(this.menu){
35063         this.menu = Roo.menu.MenuMgr.get(this.menu);
35064     }
35065 };
35066 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35067     
35068     /**
35069      * @cfg {String} text
35070      * The text to show on the menu item.
35071      */
35072     text: '',
35073      /**
35074      * @cfg {String} HTML to render in menu
35075      * The text to show on the menu item (HTML version).
35076      */
35077     html: '',
35078     /**
35079      * @cfg {String} icon
35080      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35081      */
35082     icon: undefined,
35083     /**
35084      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35085      */
35086     itemCls : "x-menu-item",
35087     /**
35088      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35089      */
35090     canActivate : true,
35091     /**
35092      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35093      */
35094     showDelay: 200,
35095     // doc'd in BaseItem
35096     hideDelay: 200,
35097
35098     // private
35099     ctype: "Roo.menu.Item",
35100     
35101     // private
35102     onRender : function(container, position){
35103         var el = document.createElement("a");
35104         el.hideFocus = true;
35105         el.unselectable = "on";
35106         el.href = this.href || "#";
35107         if(this.hrefTarget){
35108             el.target = this.hrefTarget;
35109         }
35110         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35111         
35112         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35113         
35114         el.innerHTML = String.format(
35115                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35116                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35117         this.el = el;
35118         Roo.menu.Item.superclass.onRender.call(this, container, position);
35119     },
35120
35121     /**
35122      * Sets the text to display in this menu item
35123      * @param {String} text The text to display
35124      * @param {Boolean} isHTML true to indicate text is pure html.
35125      */
35126     setText : function(text, isHTML){
35127         if (isHTML) {
35128             this.html = text;
35129         } else {
35130             this.text = text;
35131             this.html = '';
35132         }
35133         if(this.rendered){
35134             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35135      
35136             this.el.update(String.format(
35137                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35138                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35139             this.parentMenu.autoWidth();
35140         }
35141     },
35142
35143     // private
35144     handleClick : function(e){
35145         if(!this.href){ // if no link defined, stop the event automatically
35146             e.stopEvent();
35147         }
35148         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35149     },
35150
35151     // private
35152     activate : function(autoExpand){
35153         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35154             this.focus();
35155             if(autoExpand){
35156                 this.expandMenu();
35157             }
35158         }
35159         return true;
35160     },
35161
35162     // private
35163     shouldDeactivate : function(e){
35164         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35165             if(this.menu && this.menu.isVisible()){
35166                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35167             }
35168             return true;
35169         }
35170         return false;
35171     },
35172
35173     // private
35174     deactivate : function(){
35175         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35176         this.hideMenu();
35177     },
35178
35179     // private
35180     expandMenu : function(autoActivate){
35181         if(!this.disabled && this.menu){
35182             clearTimeout(this.hideTimer);
35183             delete this.hideTimer;
35184             if(!this.menu.isVisible() && !this.showTimer){
35185                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35186             }else if (this.menu.isVisible() && autoActivate){
35187                 this.menu.tryActivate(0, 1);
35188             }
35189         }
35190     },
35191
35192     // private
35193     deferExpand : function(autoActivate){
35194         delete this.showTimer;
35195         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35196         if(autoActivate){
35197             this.menu.tryActivate(0, 1);
35198         }
35199     },
35200
35201     // private
35202     hideMenu : function(){
35203         clearTimeout(this.showTimer);
35204         delete this.showTimer;
35205         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35206             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35207         }
35208     },
35209
35210     // private
35211     deferHide : function(){
35212         delete this.hideTimer;
35213         this.menu.hide();
35214     }
35215 });/*
35216  * Based on:
35217  * Ext JS Library 1.1.1
35218  * Copyright(c) 2006-2007, Ext JS, LLC.
35219  *
35220  * Originally Released Under LGPL - original licence link has changed is not relivant.
35221  *
35222  * Fork - LGPL
35223  * <script type="text/javascript">
35224  */
35225  
35226 /**
35227  * @class Roo.menu.CheckItem
35228  * @extends Roo.menu.Item
35229  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35230  * @constructor
35231  * Creates a new CheckItem
35232  * @param {Object} config Configuration options
35233  */
35234 Roo.menu.CheckItem = function(config){
35235     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35236     this.addEvents({
35237         /**
35238          * @event beforecheckchange
35239          * Fires before the checked value is set, providing an opportunity to cancel if needed
35240          * @param {Roo.menu.CheckItem} this
35241          * @param {Boolean} checked The new checked value that will be set
35242          */
35243         "beforecheckchange" : true,
35244         /**
35245          * @event checkchange
35246          * Fires after the checked value has been set
35247          * @param {Roo.menu.CheckItem} this
35248          * @param {Boolean} checked The checked value that was set
35249          */
35250         "checkchange" : true
35251     });
35252     if(this.checkHandler){
35253         this.on('checkchange', this.checkHandler, this.scope);
35254     }
35255 };
35256 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35257     /**
35258      * @cfg {String} group
35259      * All check items with the same group name will automatically be grouped into a single-select
35260      * radio button group (defaults to '')
35261      */
35262     /**
35263      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35264      */
35265     itemCls : "x-menu-item x-menu-check-item",
35266     /**
35267      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35268      */
35269     groupClass : "x-menu-group-item",
35270
35271     /**
35272      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35273      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35274      * initialized with checked = true will be rendered as checked.
35275      */
35276     checked: false,
35277
35278     // private
35279     ctype: "Roo.menu.CheckItem",
35280
35281     // private
35282     onRender : function(c){
35283         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35284         if(this.group){
35285             this.el.addClass(this.groupClass);
35286         }
35287         Roo.menu.MenuMgr.registerCheckable(this);
35288         if(this.checked){
35289             this.checked = false;
35290             this.setChecked(true, true);
35291         }
35292     },
35293
35294     // private
35295     destroy : function(){
35296         if(this.rendered){
35297             Roo.menu.MenuMgr.unregisterCheckable(this);
35298         }
35299         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35300     },
35301
35302     /**
35303      * Set the checked state of this item
35304      * @param {Boolean} checked The new checked value
35305      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35306      */
35307     setChecked : function(state, suppressEvent){
35308         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35309             if(this.container){
35310                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35311             }
35312             this.checked = state;
35313             if(suppressEvent !== true){
35314                 this.fireEvent("checkchange", this, state);
35315             }
35316         }
35317     },
35318
35319     // private
35320     handleClick : function(e){
35321        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35322            this.setChecked(!this.checked);
35323        }
35324        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35325     }
35326 });/*
35327  * Based on:
35328  * Ext JS Library 1.1.1
35329  * Copyright(c) 2006-2007, Ext JS, LLC.
35330  *
35331  * Originally Released Under LGPL - original licence link has changed is not relivant.
35332  *
35333  * Fork - LGPL
35334  * <script type="text/javascript">
35335  */
35336  
35337 /**
35338  * @class Roo.menu.DateItem
35339  * @extends Roo.menu.Adapter
35340  * A menu item that wraps the {@link Roo.DatPicker} component.
35341  * @constructor
35342  * Creates a new DateItem
35343  * @param {Object} config Configuration options
35344  */
35345 Roo.menu.DateItem = function(config){
35346     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35347     /** The Roo.DatePicker object @type Roo.DatePicker */
35348     this.picker = this.component;
35349     this.addEvents({select: true});
35350     
35351     this.picker.on("render", function(picker){
35352         picker.getEl().swallowEvent("click");
35353         picker.container.addClass("x-menu-date-item");
35354     });
35355
35356     this.picker.on("select", this.onSelect, this);
35357 };
35358
35359 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35360     // private
35361     onSelect : function(picker, date){
35362         this.fireEvent("select", this, date, picker);
35363         Roo.menu.DateItem.superclass.handleClick.call(this);
35364     }
35365 });/*
35366  * Based on:
35367  * Ext JS Library 1.1.1
35368  * Copyright(c) 2006-2007, Ext JS, LLC.
35369  *
35370  * Originally Released Under LGPL - original licence link has changed is not relivant.
35371  *
35372  * Fork - LGPL
35373  * <script type="text/javascript">
35374  */
35375  
35376 /**
35377  * @class Roo.menu.ColorItem
35378  * @extends Roo.menu.Adapter
35379  * A menu item that wraps the {@link Roo.ColorPalette} component.
35380  * @constructor
35381  * Creates a new ColorItem
35382  * @param {Object} config Configuration options
35383  */
35384 Roo.menu.ColorItem = function(config){
35385     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35386     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35387     this.palette = this.component;
35388     this.relayEvents(this.palette, ["select"]);
35389     if(this.selectHandler){
35390         this.on('select', this.selectHandler, this.scope);
35391     }
35392 };
35393 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35394  * Based on:
35395  * Ext JS Library 1.1.1
35396  * Copyright(c) 2006-2007, Ext JS, LLC.
35397  *
35398  * Originally Released Under LGPL - original licence link has changed is not relivant.
35399  *
35400  * Fork - LGPL
35401  * <script type="text/javascript">
35402  */
35403  
35404
35405 /**
35406  * @class Roo.menu.DateMenu
35407  * @extends Roo.menu.Menu
35408  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35409  * @constructor
35410  * Creates a new DateMenu
35411  * @param {Object} config Configuration options
35412  */
35413 Roo.menu.DateMenu = function(config){
35414     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35415     this.plain = true;
35416     var di = new Roo.menu.DateItem(config);
35417     this.add(di);
35418     /**
35419      * The {@link Roo.DatePicker} instance for this DateMenu
35420      * @type DatePicker
35421      */
35422     this.picker = di.picker;
35423     /**
35424      * @event select
35425      * @param {DatePicker} picker
35426      * @param {Date} date
35427      */
35428     this.relayEvents(di, ["select"]);
35429
35430     this.on('beforeshow', function(){
35431         if(this.picker){
35432             this.picker.hideMonthPicker(true);
35433         }
35434     }, this);
35435 };
35436 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35437     cls:'x-date-menu'
35438 });/*
35439  * Based on:
35440  * Ext JS Library 1.1.1
35441  * Copyright(c) 2006-2007, Ext JS, LLC.
35442  *
35443  * Originally Released Under LGPL - original licence link has changed is not relivant.
35444  *
35445  * Fork - LGPL
35446  * <script type="text/javascript">
35447  */
35448  
35449
35450 /**
35451  * @class Roo.menu.ColorMenu
35452  * @extends Roo.menu.Menu
35453  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35454  * @constructor
35455  * Creates a new ColorMenu
35456  * @param {Object} config Configuration options
35457  */
35458 Roo.menu.ColorMenu = function(config){
35459     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35460     this.plain = true;
35461     var ci = new Roo.menu.ColorItem(config);
35462     this.add(ci);
35463     /**
35464      * The {@link Roo.ColorPalette} instance for this ColorMenu
35465      * @type ColorPalette
35466      */
35467     this.palette = ci.palette;
35468     /**
35469      * @event select
35470      * @param {ColorPalette} palette
35471      * @param {String} color
35472      */
35473     this.relayEvents(ci, ["select"]);
35474 };
35475 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35476  * Based on:
35477  * Ext JS Library 1.1.1
35478  * Copyright(c) 2006-2007, Ext JS, LLC.
35479  *
35480  * Originally Released Under LGPL - original licence link has changed is not relivant.
35481  *
35482  * Fork - LGPL
35483  * <script type="text/javascript">
35484  */
35485  
35486 /**
35487  * @class Roo.form.Field
35488  * @extends Roo.BoxComponent
35489  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35490  * @constructor
35491  * Creates a new Field
35492  * @param {Object} config Configuration options
35493  */
35494 Roo.form.Field = function(config){
35495     Roo.form.Field.superclass.constructor.call(this, config);
35496 };
35497
35498 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35499     /**
35500      * @cfg {String} fieldLabel Label to use when rendering a form.
35501      */
35502        /**
35503      * @cfg {String} qtip Mouse over tip
35504      */
35505      
35506     /**
35507      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35508      */
35509     invalidClass : "x-form-invalid",
35510     /**
35511      * @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")
35512      */
35513     invalidText : "The value in this field is invalid",
35514     /**
35515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35516      */
35517     focusClass : "x-form-focus",
35518     /**
35519      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35520       automatic validation (defaults to "keyup").
35521      */
35522     validationEvent : "keyup",
35523     /**
35524      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35525      */
35526     validateOnBlur : true,
35527     /**
35528      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35529      */
35530     validationDelay : 250,
35531     /**
35532      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35533      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35534      */
35535     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35536     /**
35537      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35538      */
35539     fieldClass : "x-form-field",
35540     /**
35541      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35542      *<pre>
35543 Value         Description
35544 -----------   ----------------------------------------------------------------------
35545 qtip          Display a quick tip when the user hovers over the field
35546 title         Display a default browser title attribute popup
35547 under         Add a block div beneath the field containing the error text
35548 side          Add an error icon to the right of the field with a popup on hover
35549 [element id]  Add the error text directly to the innerHTML of the specified element
35550 </pre>
35551      */
35552     msgTarget : 'qtip',
35553     /**
35554      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35555      */
35556     msgFx : 'normal',
35557
35558     /**
35559      * @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.
35560      */
35561     readOnly : false,
35562
35563     /**
35564      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35565      */
35566     disabled : false,
35567
35568     /**
35569      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35570      */
35571     inputType : undefined,
35572     
35573     /**
35574      * @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).
35575          */
35576         tabIndex : undefined,
35577         
35578     // private
35579     isFormField : true,
35580
35581     // private
35582     hasFocus : false,
35583     /**
35584      * @property {Roo.Element} fieldEl
35585      * Element Containing the rendered Field (with label etc.)
35586      */
35587     /**
35588      * @cfg {Mixed} value A value to initialize this field with.
35589      */
35590     value : undefined,
35591
35592     /**
35593      * @cfg {String} name The field's HTML name attribute.
35594      */
35595     /**
35596      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35597      */
35598
35599         // private ??
35600         initComponent : function(){
35601         Roo.form.Field.superclass.initComponent.call(this);
35602         this.addEvents({
35603             /**
35604              * @event focus
35605              * Fires when this field receives input focus.
35606              * @param {Roo.form.Field} this
35607              */
35608             focus : true,
35609             /**
35610              * @event blur
35611              * Fires when this field loses input focus.
35612              * @param {Roo.form.Field} this
35613              */
35614             blur : true,
35615             /**
35616              * @event specialkey
35617              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35618              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35619              * @param {Roo.form.Field} this
35620              * @param {Roo.EventObject} e The event object
35621              */
35622             specialkey : true,
35623             /**
35624              * @event change
35625              * Fires just before the field blurs if the field value has changed.
35626              * @param {Roo.form.Field} this
35627              * @param {Mixed} newValue The new value
35628              * @param {Mixed} oldValue The original value
35629              */
35630             change : true,
35631             /**
35632              * @event invalid
35633              * Fires after the field has been marked as invalid.
35634              * @param {Roo.form.Field} this
35635              * @param {String} msg The validation message
35636              */
35637             invalid : true,
35638             /**
35639              * @event valid
35640              * Fires after the field has been validated with no errors.
35641              * @param {Roo.form.Field} this
35642              */
35643             valid : true,
35644              /**
35645              * @event keyup
35646              * Fires after the key up
35647              * @param {Roo.form.Field} this
35648              * @param {Roo.EventObject}  e The event Object
35649              */
35650             keyup : true
35651         });
35652     },
35653
35654     /**
35655      * Returns the name attribute of the field if available
35656      * @return {String} name The field name
35657      */
35658     getName: function(){
35659          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35660     },
35661
35662     // private
35663     onRender : function(ct, position){
35664         Roo.form.Field.superclass.onRender.call(this, ct, position);
35665         if(!this.el){
35666             var cfg = this.getAutoCreate();
35667             if(!cfg.name){
35668                 cfg.name = this.name || this.id;
35669             }
35670             if(this.inputType){
35671                 cfg.type = this.inputType;
35672             }
35673             this.el = ct.createChild(cfg, position);
35674         }
35675         var type = this.el.dom.type;
35676         if(type){
35677             if(type == 'password'){
35678                 type = 'text';
35679             }
35680             this.el.addClass('x-form-'+type);
35681         }
35682         if(this.readOnly){
35683             this.el.dom.readOnly = true;
35684         }
35685         if(this.tabIndex !== undefined){
35686             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35687         }
35688
35689         this.el.addClass([this.fieldClass, this.cls]);
35690         this.initValue();
35691     },
35692
35693     /**
35694      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35695      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35696      * @return {Roo.form.Field} this
35697      */
35698     applyTo : function(target){
35699         this.allowDomMove = false;
35700         this.el = Roo.get(target);
35701         this.render(this.el.dom.parentNode);
35702         return this;
35703     },
35704
35705     // private
35706     initValue : function(){
35707         if(this.value !== undefined){
35708             this.setValue(this.value);
35709         }else if(this.el.dom.value.length > 0){
35710             this.setValue(this.el.dom.value);
35711         }
35712     },
35713
35714     /**
35715      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35716      */
35717     isDirty : function() {
35718         if(this.disabled) {
35719             return false;
35720         }
35721         return String(this.getValue()) !== String(this.originalValue);
35722     },
35723
35724     // private
35725     afterRender : function(){
35726         Roo.form.Field.superclass.afterRender.call(this);
35727         this.initEvents();
35728     },
35729
35730     // private
35731     fireKey : function(e){
35732         //Roo.log('field ' + e.getKey());
35733         if(e.isNavKeyPress()){
35734             this.fireEvent("specialkey", this, e);
35735         }
35736     },
35737
35738     /**
35739      * Resets the current field value to the originally loaded value and clears any validation messages
35740      */
35741     reset : function(){
35742         this.setValue(this.originalValue);
35743         this.clearInvalid();
35744     },
35745
35746     // private
35747     initEvents : function(){
35748         // safari killled keypress - so keydown is now used..
35749         this.el.on("keydown" , this.fireKey,  this);
35750         this.el.on("focus", this.onFocus,  this);
35751         this.el.on("blur", this.onBlur,  this);
35752         this.el.relayEvent('keyup', this);
35753
35754         // reference to original value for reset
35755         this.originalValue = this.getValue();
35756     },
35757
35758     // private
35759     onFocus : function(){
35760         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35761             this.el.addClass(this.focusClass);
35762         }
35763         if(!this.hasFocus){
35764             this.hasFocus = true;
35765             this.startValue = this.getValue();
35766             this.fireEvent("focus", this);
35767         }
35768     },
35769
35770     beforeBlur : Roo.emptyFn,
35771
35772     // private
35773     onBlur : function(){
35774         this.beforeBlur();
35775         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35776             this.el.removeClass(this.focusClass);
35777         }
35778         this.hasFocus = false;
35779         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35780             this.validate();
35781         }
35782         var v = this.getValue();
35783         if(String(v) !== String(this.startValue)){
35784             this.fireEvent('change', this, v, this.startValue);
35785         }
35786         this.fireEvent("blur", this);
35787     },
35788
35789     /**
35790      * Returns whether or not the field value is currently valid
35791      * @param {Boolean} preventMark True to disable marking the field invalid
35792      * @return {Boolean} True if the value is valid, else false
35793      */
35794     isValid : function(preventMark){
35795         if(this.disabled){
35796             return true;
35797         }
35798         var restore = this.preventMark;
35799         this.preventMark = preventMark === true;
35800         var v = this.validateValue(this.processValue(this.getRawValue()));
35801         this.preventMark = restore;
35802         return v;
35803     },
35804
35805     /**
35806      * Validates the field value
35807      * @return {Boolean} True if the value is valid, else false
35808      */
35809     validate : function(){
35810         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35811             this.clearInvalid();
35812             return true;
35813         }
35814         return false;
35815     },
35816
35817     processValue : function(value){
35818         return value;
35819     },
35820
35821     // private
35822     // Subclasses should provide the validation implementation by overriding this
35823     validateValue : function(value){
35824         return true;
35825     },
35826
35827     /**
35828      * Mark this field as invalid
35829      * @param {String} msg The validation message
35830      */
35831     markInvalid : function(msg){
35832         if(!this.rendered || this.preventMark){ // not rendered
35833             return;
35834         }
35835         this.el.addClass(this.invalidClass);
35836         msg = msg || this.invalidText;
35837         switch(this.msgTarget){
35838             case 'qtip':
35839                 this.el.dom.qtip = msg;
35840                 this.el.dom.qclass = 'x-form-invalid-tip';
35841                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35842                     Roo.QuickTips.enable();
35843                 }
35844                 break;
35845             case 'title':
35846                 this.el.dom.title = msg;
35847                 break;
35848             case 'under':
35849                 if(!this.errorEl){
35850                     var elp = this.el.findParent('.x-form-element', 5, true);
35851                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35852                     this.errorEl.setWidth(elp.getWidth(true)-20);
35853                 }
35854                 this.errorEl.update(msg);
35855                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35856                 break;
35857             case 'side':
35858                 if(!this.errorIcon){
35859                     var elp = this.el.findParent('.x-form-element', 5, true);
35860                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35861                 }
35862                 this.alignErrorIcon();
35863                 this.errorIcon.dom.qtip = msg;
35864                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35865                 this.errorIcon.show();
35866                 this.on('resize', this.alignErrorIcon, this);
35867                 break;
35868             default:
35869                 var t = Roo.getDom(this.msgTarget);
35870                 t.innerHTML = msg;
35871                 t.style.display = this.msgDisplay;
35872                 break;
35873         }
35874         this.fireEvent('invalid', this, msg);
35875     },
35876
35877     // private
35878     alignErrorIcon : function(){
35879         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35880     },
35881
35882     /**
35883      * Clear any invalid styles/messages for this field
35884      */
35885     clearInvalid : function(){
35886         if(!this.rendered || this.preventMark){ // not rendered
35887             return;
35888         }
35889         this.el.removeClass(this.invalidClass);
35890         switch(this.msgTarget){
35891             case 'qtip':
35892                 this.el.dom.qtip = '';
35893                 break;
35894             case 'title':
35895                 this.el.dom.title = '';
35896                 break;
35897             case 'under':
35898                 if(this.errorEl){
35899                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35900                 }
35901                 break;
35902             case 'side':
35903                 if(this.errorIcon){
35904                     this.errorIcon.dom.qtip = '';
35905                     this.errorIcon.hide();
35906                     this.un('resize', this.alignErrorIcon, this);
35907                 }
35908                 break;
35909             default:
35910                 var t = Roo.getDom(this.msgTarget);
35911                 t.innerHTML = '';
35912                 t.style.display = 'none';
35913                 break;
35914         }
35915         this.fireEvent('valid', this);
35916     },
35917
35918     /**
35919      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35920      * @return {Mixed} value The field value
35921      */
35922     getRawValue : function(){
35923         var v = this.el.getValue();
35924         if(v === this.emptyText){
35925             v = '';
35926         }
35927         return v;
35928     },
35929
35930     /**
35931      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35932      * @return {Mixed} value The field value
35933      */
35934     getValue : function(){
35935         var v = this.el.getValue();
35936         if(v === this.emptyText || v === undefined){
35937             v = '';
35938         }
35939         return v;
35940     },
35941
35942     /**
35943      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35944      * @param {Mixed} value The value to set
35945      */
35946     setRawValue : function(v){
35947         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35948     },
35949
35950     /**
35951      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35952      * @param {Mixed} value The value to set
35953      */
35954     setValue : function(v){
35955         this.value = v;
35956         if(this.rendered){
35957             this.el.dom.value = (v === null || v === undefined ? '' : v);
35958             this.validate();
35959         }
35960     },
35961
35962     adjustSize : function(w, h){
35963         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35964         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35965         return s;
35966     },
35967
35968     adjustWidth : function(tag, w){
35969         tag = tag.toLowerCase();
35970         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35971             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35972                 if(tag == 'input'){
35973                     return w + 2;
35974                 }
35975                 if(tag = 'textarea'){
35976                     return w-2;
35977                 }
35978             }else if(Roo.isOpera){
35979                 if(tag == 'input'){
35980                     return w + 2;
35981                 }
35982                 if(tag = 'textarea'){
35983                     return w-2;
35984                 }
35985             }
35986         }
35987         return w;
35988     }
35989 });
35990
35991
35992 // anything other than normal should be considered experimental
35993 Roo.form.Field.msgFx = {
35994     normal : {
35995         show: function(msgEl, f){
35996             msgEl.setDisplayed('block');
35997         },
35998
35999         hide : function(msgEl, f){
36000             msgEl.setDisplayed(false).update('');
36001         }
36002     },
36003
36004     slide : {
36005         show: function(msgEl, f){
36006             msgEl.slideIn('t', {stopFx:true});
36007         },
36008
36009         hide : function(msgEl, f){
36010             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36011         }
36012     },
36013
36014     slideRight : {
36015         show: function(msgEl, f){
36016             msgEl.fixDisplay();
36017             msgEl.alignTo(f.el, 'tl-tr');
36018             msgEl.slideIn('l', {stopFx:true});
36019         },
36020
36021         hide : function(msgEl, f){
36022             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36023         }
36024     }
36025 };/*
36026  * Based on:
36027  * Ext JS Library 1.1.1
36028  * Copyright(c) 2006-2007, Ext JS, LLC.
36029  *
36030  * Originally Released Under LGPL - original licence link has changed is not relivant.
36031  *
36032  * Fork - LGPL
36033  * <script type="text/javascript">
36034  */
36035  
36036
36037 /**
36038  * @class Roo.form.TextField
36039  * @extends Roo.form.Field
36040  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36041  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36042  * @constructor
36043  * Creates a new TextField
36044  * @param {Object} config Configuration options
36045  */
36046 Roo.form.TextField = function(config){
36047     Roo.form.TextField.superclass.constructor.call(this, config);
36048     this.addEvents({
36049         /**
36050          * @event autosize
36051          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36052          * according to the default logic, but this event provides a hook for the developer to apply additional
36053          * logic at runtime to resize the field if needed.
36054              * @param {Roo.form.Field} this This text field
36055              * @param {Number} width The new field width
36056              */
36057         autosize : true
36058     });
36059 };
36060
36061 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36062     /**
36063      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36064      */
36065     grow : false,
36066     /**
36067      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36068      */
36069     growMin : 30,
36070     /**
36071      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36072      */
36073     growMax : 800,
36074     /**
36075      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36076      */
36077     vtype : null,
36078     /**
36079      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36080      */
36081     maskRe : null,
36082     /**
36083      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36084      */
36085     disableKeyFilter : false,
36086     /**
36087      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36088      */
36089     allowBlank : true,
36090     /**
36091      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36092      */
36093     minLength : 0,
36094     /**
36095      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36096      */
36097     maxLength : Number.MAX_VALUE,
36098     /**
36099      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36100      */
36101     minLengthText : "The minimum length for this field is {0}",
36102     /**
36103      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36104      */
36105     maxLengthText : "The maximum length for this field is {0}",
36106     /**
36107      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36108      */
36109     selectOnFocus : false,
36110     /**
36111      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36112      */
36113     blankText : "This field is required",
36114     /**
36115      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36116      * If available, this function will be called only after the basic validators all return true, and will be passed the
36117      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36118      */
36119     validator : null,
36120     /**
36121      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36122      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36123      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36124      */
36125     regex : null,
36126     /**
36127      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36128      */
36129     regexText : "",
36130     /**
36131      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36132      */
36133     emptyText : null,
36134     /**
36135      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36136      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36137      */
36138     emptyClass : 'x-form-empty-field',
36139
36140     // private
36141     initEvents : function(){
36142         Roo.form.TextField.superclass.initEvents.call(this);
36143         if(this.validationEvent == 'keyup'){
36144             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36145             this.el.on('keyup', this.filterValidation, this);
36146         }
36147         else if(this.validationEvent !== false){
36148             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36149         }
36150         if(this.selectOnFocus || this.emptyText){
36151             this.on("focus", this.preFocus, this);
36152             if(this.emptyText){
36153                 this.on('blur', this.postBlur, this);
36154                 this.applyEmptyText();
36155             }
36156         }
36157         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36158             this.el.on("keypress", this.filterKeys, this);
36159         }
36160         if(this.grow){
36161             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36162             this.el.on("click", this.autoSize,  this);
36163         }
36164     },
36165
36166     processValue : function(value){
36167         if(this.stripCharsRe){
36168             var newValue = value.replace(this.stripCharsRe, '');
36169             if(newValue !== value){
36170                 this.setRawValue(newValue);
36171                 return newValue;
36172             }
36173         }
36174         return value;
36175     },
36176
36177     filterValidation : function(e){
36178         if(!e.isNavKeyPress()){
36179             this.validationTask.delay(this.validationDelay);
36180         }
36181     },
36182
36183     // private
36184     onKeyUp : function(e){
36185         if(!e.isNavKeyPress()){
36186             this.autoSize();
36187         }
36188     },
36189
36190     /**
36191      * Resets the current field value to the originally-loaded value and clears any validation messages.
36192      * Also adds emptyText and emptyClass if the original value was blank.
36193      */
36194     reset : function(){
36195         Roo.form.TextField.superclass.reset.call(this);
36196         this.applyEmptyText();
36197     },
36198
36199     applyEmptyText : function(){
36200         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36201             this.setRawValue(this.emptyText);
36202             this.el.addClass(this.emptyClass);
36203         }
36204     },
36205
36206     // private
36207     preFocus : function(){
36208         if(this.emptyText){
36209             if(this.el.dom.value == this.emptyText){
36210                 this.setRawValue('');
36211             }
36212             this.el.removeClass(this.emptyClass);
36213         }
36214         if(this.selectOnFocus){
36215             this.el.dom.select();
36216         }
36217     },
36218
36219     // private
36220     postBlur : function(){
36221         this.applyEmptyText();
36222     },
36223
36224     // private
36225     filterKeys : function(e){
36226         var k = e.getKey();
36227         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36228             return;
36229         }
36230         var c = e.getCharCode(), cc = String.fromCharCode(c);
36231         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36232             return;
36233         }
36234         if(!this.maskRe.test(cc)){
36235             e.stopEvent();
36236         }
36237     },
36238
36239     setValue : function(v){
36240         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36241             this.el.removeClass(this.emptyClass);
36242         }
36243         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36244         this.applyEmptyText();
36245         this.autoSize();
36246     },
36247
36248     /**
36249      * Validates a value according to the field's validation rules and marks the field as invalid
36250      * if the validation fails
36251      * @param {Mixed} value The value to validate
36252      * @return {Boolean} True if the value is valid, else false
36253      */
36254     validateValue : function(value){
36255         if(value.length < 1 || value === this.emptyText){ // if it's blank
36256              if(this.allowBlank){
36257                 this.clearInvalid();
36258                 return true;
36259              }else{
36260                 this.markInvalid(this.blankText);
36261                 return false;
36262              }
36263         }
36264         if(value.length < this.minLength){
36265             this.markInvalid(String.format(this.minLengthText, this.minLength));
36266             return false;
36267         }
36268         if(value.length > this.maxLength){
36269             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36270             return false;
36271         }
36272         if(this.vtype){
36273             var vt = Roo.form.VTypes;
36274             if(!vt[this.vtype](value, this)){
36275                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36276                 return false;
36277             }
36278         }
36279         if(typeof this.validator == "function"){
36280             var msg = this.validator(value);
36281             if(msg !== true){
36282                 this.markInvalid(msg);
36283                 return false;
36284             }
36285         }
36286         if(this.regex && !this.regex.test(value)){
36287             this.markInvalid(this.regexText);
36288             return false;
36289         }
36290         return true;
36291     },
36292
36293     /**
36294      * Selects text in this field
36295      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36296      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36297      */
36298     selectText : function(start, end){
36299         var v = this.getRawValue();
36300         if(v.length > 0){
36301             start = start === undefined ? 0 : start;
36302             end = end === undefined ? v.length : end;
36303             var d = this.el.dom;
36304             if(d.setSelectionRange){
36305                 d.setSelectionRange(start, end);
36306             }else if(d.createTextRange){
36307                 var range = d.createTextRange();
36308                 range.moveStart("character", start);
36309                 range.moveEnd("character", v.length-end);
36310                 range.select();
36311             }
36312         }
36313     },
36314
36315     /**
36316      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36317      * This only takes effect if grow = true, and fires the autosize event.
36318      */
36319     autoSize : function(){
36320         if(!this.grow || !this.rendered){
36321             return;
36322         }
36323         if(!this.metrics){
36324             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36325         }
36326         var el = this.el;
36327         var v = el.dom.value;
36328         var d = document.createElement('div');
36329         d.appendChild(document.createTextNode(v));
36330         v = d.innerHTML;
36331         d = null;
36332         v += "&#160;";
36333         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36334         this.el.setWidth(w);
36335         this.fireEvent("autosize", this, w);
36336     }
36337 });/*
36338  * Based on:
36339  * Ext JS Library 1.1.1
36340  * Copyright(c) 2006-2007, Ext JS, LLC.
36341  *
36342  * Originally Released Under LGPL - original licence link has changed is not relivant.
36343  *
36344  * Fork - LGPL
36345  * <script type="text/javascript">
36346  */
36347  
36348 /**
36349  * @class Roo.form.Hidden
36350  * @extends Roo.form.TextField
36351  * Simple Hidden element used on forms 
36352  * 
36353  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36354  * 
36355  * @constructor
36356  * Creates a new Hidden form element.
36357  * @param {Object} config Configuration options
36358  */
36359
36360
36361
36362 // easy hidden field...
36363 Roo.form.Hidden = function(config){
36364     Roo.form.Hidden.superclass.constructor.call(this, config);
36365 };
36366   
36367 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36368     fieldLabel:      '',
36369     inputType:      'hidden',
36370     width:          50,
36371     allowBlank:     true,
36372     labelSeparator: '',
36373     hidden:         true,
36374     itemCls :       'x-form-item-display-none'
36375
36376
36377 });
36378
36379
36380 /*
36381  * Based on:
36382  * Ext JS Library 1.1.1
36383  * Copyright(c) 2006-2007, Ext JS, LLC.
36384  *
36385  * Originally Released Under LGPL - original licence link has changed is not relivant.
36386  *
36387  * Fork - LGPL
36388  * <script type="text/javascript">
36389  */
36390  
36391 /**
36392  * @class Roo.form.TriggerField
36393  * @extends Roo.form.TextField
36394  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36395  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36396  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36397  * for which you can provide a custom implementation.  For example:
36398  * <pre><code>
36399 var trigger = new Roo.form.TriggerField();
36400 trigger.onTriggerClick = myTriggerFn;
36401 trigger.applyTo('my-field');
36402 </code></pre>
36403  *
36404  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36405  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36406  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36407  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36408  * @constructor
36409  * Create a new TriggerField.
36410  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36411  * to the base TextField)
36412  */
36413 Roo.form.TriggerField = function(config){
36414     this.mimicing = false;
36415     Roo.form.TriggerField.superclass.constructor.call(this, config);
36416 };
36417
36418 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36419     /**
36420      * @cfg {String} triggerClass A CSS class to apply to the trigger
36421      */
36422     /**
36423      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36424      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36425      */
36426     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36427     /**
36428      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36429      */
36430     hideTrigger:false,
36431
36432     /** @cfg {Boolean} grow @hide */
36433     /** @cfg {Number} growMin @hide */
36434     /** @cfg {Number} growMax @hide */
36435
36436     /**
36437      * @hide 
36438      * @method
36439      */
36440     autoSize: Roo.emptyFn,
36441     // private
36442     monitorTab : true,
36443     // private
36444     deferHeight : true,
36445
36446     
36447     actionMode : 'wrap',
36448     // private
36449     onResize : function(w, h){
36450         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36451         if(typeof w == 'number'){
36452             var x = w - this.trigger.getWidth();
36453             this.el.setWidth(this.adjustWidth('input', x));
36454             this.trigger.setStyle('left', x+'px');
36455         }
36456     },
36457
36458     // private
36459     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36460
36461     // private
36462     getResizeEl : function(){
36463         return this.wrap;
36464     },
36465
36466     // private
36467     getPositionEl : function(){
36468         return this.wrap;
36469     },
36470
36471     // private
36472     alignErrorIcon : function(){
36473         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36474     },
36475
36476     // private
36477     onRender : function(ct, position){
36478         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36479         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36480         this.trigger = this.wrap.createChild(this.triggerConfig ||
36481                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36482         if(this.hideTrigger){
36483             this.trigger.setDisplayed(false);
36484         }
36485         this.initTrigger();
36486         if(!this.width){
36487             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36488         }
36489     },
36490
36491     // private
36492     initTrigger : function(){
36493         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36494         this.trigger.addClassOnOver('x-form-trigger-over');
36495         this.trigger.addClassOnClick('x-form-trigger-click');
36496     },
36497
36498     // private
36499     onDestroy : function(){
36500         if(this.trigger){
36501             this.trigger.removeAllListeners();
36502             this.trigger.remove();
36503         }
36504         if(this.wrap){
36505             this.wrap.remove();
36506         }
36507         Roo.form.TriggerField.superclass.onDestroy.call(this);
36508     },
36509
36510     // private
36511     onFocus : function(){
36512         Roo.form.TriggerField.superclass.onFocus.call(this);
36513         if(!this.mimicing){
36514             this.wrap.addClass('x-trigger-wrap-focus');
36515             this.mimicing = true;
36516             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36517             if(this.monitorTab){
36518                 this.el.on("keydown", this.checkTab, this);
36519             }
36520         }
36521     },
36522
36523     // private
36524     checkTab : function(e){
36525         if(e.getKey() == e.TAB){
36526             this.triggerBlur();
36527         }
36528     },
36529
36530     // private
36531     onBlur : function(){
36532         // do nothing
36533     },
36534
36535     // private
36536     mimicBlur : function(e, t){
36537         if(!this.wrap.contains(t) && this.validateBlur()){
36538             this.triggerBlur();
36539         }
36540     },
36541
36542     // private
36543     triggerBlur : function(){
36544         this.mimicing = false;
36545         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36546         if(this.monitorTab){
36547             this.el.un("keydown", this.checkTab, this);
36548         }
36549         this.wrap.removeClass('x-trigger-wrap-focus');
36550         Roo.form.TriggerField.superclass.onBlur.call(this);
36551     },
36552
36553     // private
36554     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36555     validateBlur : function(e, t){
36556         return true;
36557     },
36558
36559     // private
36560     onDisable : function(){
36561         Roo.form.TriggerField.superclass.onDisable.call(this);
36562         if(this.wrap){
36563             this.wrap.addClass('x-item-disabled');
36564         }
36565     },
36566
36567     // private
36568     onEnable : function(){
36569         Roo.form.TriggerField.superclass.onEnable.call(this);
36570         if(this.wrap){
36571             this.wrap.removeClass('x-item-disabled');
36572         }
36573     },
36574
36575     // private
36576     onShow : function(){
36577         var ae = this.getActionEl();
36578         
36579         if(ae){
36580             ae.dom.style.display = '';
36581             ae.dom.style.visibility = 'visible';
36582         }
36583     },
36584
36585     // private
36586     
36587     onHide : function(){
36588         var ae = this.getActionEl();
36589         ae.dom.style.display = 'none';
36590     },
36591
36592     /**
36593      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36594      * by an implementing function.
36595      * @method
36596      * @param {EventObject} e
36597      */
36598     onTriggerClick : Roo.emptyFn
36599 });
36600
36601 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36602 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36603 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36604 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36605     initComponent : function(){
36606         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36607
36608         this.triggerConfig = {
36609             tag:'span', cls:'x-form-twin-triggers', cn:[
36610             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36611             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36612         ]};
36613     },
36614
36615     getTrigger : function(index){
36616         return this.triggers[index];
36617     },
36618
36619     initTrigger : function(){
36620         var ts = this.trigger.select('.x-form-trigger', true);
36621         this.wrap.setStyle('overflow', 'hidden');
36622         var triggerField = this;
36623         ts.each(function(t, all, index){
36624             t.hide = function(){
36625                 var w = triggerField.wrap.getWidth();
36626                 this.dom.style.display = 'none';
36627                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36628             };
36629             t.show = function(){
36630                 var w = triggerField.wrap.getWidth();
36631                 this.dom.style.display = '';
36632                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36633             };
36634             var triggerIndex = 'Trigger'+(index+1);
36635
36636             if(this['hide'+triggerIndex]){
36637                 t.dom.style.display = 'none';
36638             }
36639             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36640             t.addClassOnOver('x-form-trigger-over');
36641             t.addClassOnClick('x-form-trigger-click');
36642         }, this);
36643         this.triggers = ts.elements;
36644     },
36645
36646     onTrigger1Click : Roo.emptyFn,
36647     onTrigger2Click : Roo.emptyFn
36648 });/*
36649  * Based on:
36650  * Ext JS Library 1.1.1
36651  * Copyright(c) 2006-2007, Ext JS, LLC.
36652  *
36653  * Originally Released Under LGPL - original licence link has changed is not relivant.
36654  *
36655  * Fork - LGPL
36656  * <script type="text/javascript">
36657  */
36658  
36659 /**
36660  * @class Roo.form.TextArea
36661  * @extends Roo.form.TextField
36662  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36663  * support for auto-sizing.
36664  * @constructor
36665  * Creates a new TextArea
36666  * @param {Object} config Configuration options
36667  */
36668 Roo.form.TextArea = function(config){
36669     Roo.form.TextArea.superclass.constructor.call(this, config);
36670     // these are provided exchanges for backwards compat
36671     // minHeight/maxHeight were replaced by growMin/growMax to be
36672     // compatible with TextField growing config values
36673     if(this.minHeight !== undefined){
36674         this.growMin = this.minHeight;
36675     }
36676     if(this.maxHeight !== undefined){
36677         this.growMax = this.maxHeight;
36678     }
36679 };
36680
36681 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36682     /**
36683      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36684      */
36685     growMin : 60,
36686     /**
36687      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36688      */
36689     growMax: 1000,
36690     /**
36691      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36692      * in the field (equivalent to setting overflow: hidden, defaults to false)
36693      */
36694     preventScrollbars: false,
36695     /**
36696      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36697      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36698      */
36699
36700     // private
36701     onRender : function(ct, position){
36702         if(!this.el){
36703             this.defaultAutoCreate = {
36704                 tag: "textarea",
36705                 style:"width:300px;height:60px;",
36706                 autocomplete: "off"
36707             };
36708         }
36709         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36710         if(this.grow){
36711             this.textSizeEl = Roo.DomHelper.append(document.body, {
36712                 tag: "pre", cls: "x-form-grow-sizer"
36713             });
36714             if(this.preventScrollbars){
36715                 this.el.setStyle("overflow", "hidden");
36716             }
36717             this.el.setHeight(this.growMin);
36718         }
36719     },
36720
36721     onDestroy : function(){
36722         if(this.textSizeEl){
36723             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36724         }
36725         Roo.form.TextArea.superclass.onDestroy.call(this);
36726     },
36727
36728     // private
36729     onKeyUp : function(e){
36730         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36731             this.autoSize();
36732         }
36733     },
36734
36735     /**
36736      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36737      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36738      */
36739     autoSize : function(){
36740         if(!this.grow || !this.textSizeEl){
36741             return;
36742         }
36743         var el = this.el;
36744         var v = el.dom.value;
36745         var ts = this.textSizeEl;
36746
36747         ts.innerHTML = '';
36748         ts.appendChild(document.createTextNode(v));
36749         v = ts.innerHTML;
36750
36751         Roo.fly(ts).setWidth(this.el.getWidth());
36752         if(v.length < 1){
36753             v = "&#160;&#160;";
36754         }else{
36755             if(Roo.isIE){
36756                 v = v.replace(/\n/g, '<p>&#160;</p>');
36757             }
36758             v += "&#160;\n&#160;";
36759         }
36760         ts.innerHTML = v;
36761         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36762         if(h != this.lastHeight){
36763             this.lastHeight = h;
36764             this.el.setHeight(h);
36765             this.fireEvent("autosize", this, h);
36766         }
36767     }
36768 });/*
36769  * Based on:
36770  * Ext JS Library 1.1.1
36771  * Copyright(c) 2006-2007, Ext JS, LLC.
36772  *
36773  * Originally Released Under LGPL - original licence link has changed is not relivant.
36774  *
36775  * Fork - LGPL
36776  * <script type="text/javascript">
36777  */
36778  
36779
36780 /**
36781  * @class Roo.form.NumberField
36782  * @extends Roo.form.TextField
36783  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36784  * @constructor
36785  * Creates a new NumberField
36786  * @param {Object} config Configuration options
36787  */
36788 Roo.form.NumberField = function(config){
36789     Roo.form.NumberField.superclass.constructor.call(this, config);
36790 };
36791
36792 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36793     /**
36794      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36795      */
36796     fieldClass: "x-form-field x-form-num-field",
36797     /**
36798      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36799      */
36800     allowDecimals : true,
36801     /**
36802      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36803      */
36804     decimalSeparator : ".",
36805     /**
36806      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36807      */
36808     decimalPrecision : 2,
36809     /**
36810      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36811      */
36812     allowNegative : true,
36813     /**
36814      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36815      */
36816     minValue : Number.NEGATIVE_INFINITY,
36817     /**
36818      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36819      */
36820     maxValue : Number.MAX_VALUE,
36821     /**
36822      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36823      */
36824     minText : "The minimum value for this field is {0}",
36825     /**
36826      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36827      */
36828     maxText : "The maximum value for this field is {0}",
36829     /**
36830      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36831      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36832      */
36833     nanText : "{0} is not a valid number",
36834
36835     // private
36836     initEvents : function(){
36837         Roo.form.NumberField.superclass.initEvents.call(this);
36838         var allowed = "0123456789";
36839         if(this.allowDecimals){
36840             allowed += this.decimalSeparator;
36841         }
36842         if(this.allowNegative){
36843             allowed += "-";
36844         }
36845         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36846         var keyPress = function(e){
36847             var k = e.getKey();
36848             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36849                 return;
36850             }
36851             var c = e.getCharCode();
36852             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36853                 e.stopEvent();
36854             }
36855         };
36856         this.el.on("keypress", keyPress, this);
36857     },
36858
36859     // private
36860     validateValue : function(value){
36861         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36862             return false;
36863         }
36864         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36865              return true;
36866         }
36867         var num = this.parseValue(value);
36868         if(isNaN(num)){
36869             this.markInvalid(String.format(this.nanText, value));
36870             return false;
36871         }
36872         if(num < this.minValue){
36873             this.markInvalid(String.format(this.minText, this.minValue));
36874             return false;
36875         }
36876         if(num > this.maxValue){
36877             this.markInvalid(String.format(this.maxText, this.maxValue));
36878             return false;
36879         }
36880         return true;
36881     },
36882
36883     getValue : function(){
36884         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36885     },
36886
36887     // private
36888     parseValue : function(value){
36889         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36890         return isNaN(value) ? '' : value;
36891     },
36892
36893     // private
36894     fixPrecision : function(value){
36895         var nan = isNaN(value);
36896         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36897             return nan ? '' : value;
36898         }
36899         return parseFloat(value).toFixed(this.decimalPrecision);
36900     },
36901
36902     setValue : function(v){
36903         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36904     },
36905
36906     // private
36907     decimalPrecisionFcn : function(v){
36908         return Math.floor(v);
36909     },
36910
36911     beforeBlur : function(){
36912         var v = this.parseValue(this.getRawValue());
36913         if(v){
36914             this.setValue(this.fixPrecision(v));
36915         }
36916     }
36917 });/*
36918  * Based on:
36919  * Ext JS Library 1.1.1
36920  * Copyright(c) 2006-2007, Ext JS, LLC.
36921  *
36922  * Originally Released Under LGPL - original licence link has changed is not relivant.
36923  *
36924  * Fork - LGPL
36925  * <script type="text/javascript">
36926  */
36927  
36928 /**
36929  * @class Roo.form.DateField
36930  * @extends Roo.form.TriggerField
36931  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36932 * @constructor
36933 * Create a new DateField
36934 * @param {Object} config
36935  */
36936 Roo.form.DateField = function(config){
36937     Roo.form.DateField.superclass.constructor.call(this, config);
36938     
36939       this.addEvents({
36940          
36941         /**
36942          * @event select
36943          * Fires when a date is selected
36944              * @param {Roo.form.DateField} combo This combo box
36945              * @param {Date} date The date selected
36946              */
36947         'select' : true
36948          
36949     });
36950     
36951     
36952     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36953     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36954     this.ddMatch = null;
36955     if(this.disabledDates){
36956         var dd = this.disabledDates;
36957         var re = "(?:";
36958         for(var i = 0; i < dd.length; i++){
36959             re += dd[i];
36960             if(i != dd.length-1) re += "|";
36961         }
36962         this.ddMatch = new RegExp(re + ")");
36963     }
36964 };
36965
36966 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36967     /**
36968      * @cfg {String} format
36969      * The default date format string which can be overriden for localization support.  The format must be
36970      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36971      */
36972     format : "m/d/y",
36973     /**
36974      * @cfg {String} altFormats
36975      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36976      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36977      */
36978     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36979     /**
36980      * @cfg {Array} disabledDays
36981      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36982      */
36983     disabledDays : null,
36984     /**
36985      * @cfg {String} disabledDaysText
36986      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36987      */
36988     disabledDaysText : "Disabled",
36989     /**
36990      * @cfg {Array} disabledDates
36991      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36992      * expression so they are very powerful. Some examples:
36993      * <ul>
36994      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36995      * <li>["03/08", "09/16"] would disable those days for every year</li>
36996      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36997      * <li>["03/../2006"] would disable every day in March 2006</li>
36998      * <li>["^03"] would disable every day in every March</li>
36999      * </ul>
37000      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37001      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37002      */
37003     disabledDates : null,
37004     /**
37005      * @cfg {String} disabledDatesText
37006      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37007      */
37008     disabledDatesText : "Disabled",
37009     /**
37010      * @cfg {Date/String} minValue
37011      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37012      * valid format (defaults to null).
37013      */
37014     minValue : null,
37015     /**
37016      * @cfg {Date/String} maxValue
37017      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37018      * valid format (defaults to null).
37019      */
37020     maxValue : null,
37021     /**
37022      * @cfg {String} minText
37023      * The error text to display when the date in the cell is before minValue (defaults to
37024      * 'The date in this field must be after {minValue}').
37025      */
37026     minText : "The date in this field must be equal to or after {0}",
37027     /**
37028      * @cfg {String} maxText
37029      * The error text to display when the date in the cell is after maxValue (defaults to
37030      * 'The date in this field must be before {maxValue}').
37031      */
37032     maxText : "The date in this field must be equal to or before {0}",
37033     /**
37034      * @cfg {String} invalidText
37035      * The error text to display when the date in the field is invalid (defaults to
37036      * '{value} is not a valid date - it must be in the format {format}').
37037      */
37038     invalidText : "{0} is not a valid date - it must be in the format {1}",
37039     /**
37040      * @cfg {String} triggerClass
37041      * An additional CSS class used to style the trigger button.  The trigger will always get the
37042      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37043      * which displays a calendar icon).
37044      */
37045     triggerClass : 'x-form-date-trigger',
37046     
37047
37048     /**
37049      * @cfg {bool} useIso
37050      * if enabled, then the date field will use a hidden field to store the 
37051      * real value as iso formated date. default (false)
37052      */ 
37053     useIso : false,
37054     /**
37055      * @cfg {String/Object} autoCreate
37056      * A DomHelper element spec, or true for a default element spec (defaults to
37057      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37058      */ 
37059     // private
37060     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37061     
37062     // private
37063     hiddenField: false,
37064     
37065     onRender : function(ct, position)
37066     {
37067         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37068         if (this.useIso) {
37069             this.el.dom.removeAttribute('name'); 
37070             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37071                     'before', true);
37072             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37073             // prevent input submission
37074             this.hiddenName = this.name;
37075         }
37076             
37077             
37078     },
37079     
37080     // private
37081     validateValue : function(value)
37082     {
37083         value = this.formatDate(value);
37084         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37085             return false;
37086         }
37087         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37088              return true;
37089         }
37090         var svalue = value;
37091         value = this.parseDate(value);
37092         if(!value){
37093             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37094             return false;
37095         }
37096         var time = value.getTime();
37097         if(this.minValue && time < this.minValue.getTime()){
37098             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37099             return false;
37100         }
37101         if(this.maxValue && time > this.maxValue.getTime()){
37102             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37103             return false;
37104         }
37105         if(this.disabledDays){
37106             var day = value.getDay();
37107             for(var i = 0; i < this.disabledDays.length; i++) {
37108                 if(day === this.disabledDays[i]){
37109                     this.markInvalid(this.disabledDaysText);
37110                     return false;
37111                 }
37112             }
37113         }
37114         var fvalue = this.formatDate(value);
37115         if(this.ddMatch && this.ddMatch.test(fvalue)){
37116             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37117             return false;
37118         }
37119         return true;
37120     },
37121
37122     // private
37123     // Provides logic to override the default TriggerField.validateBlur which just returns true
37124     validateBlur : function(){
37125         return !this.menu || !this.menu.isVisible();
37126     },
37127
37128     /**
37129      * Returns the current date value of the date field.
37130      * @return {Date} The date value
37131      */
37132     getValue : function(){
37133         
37134         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37135     },
37136
37137     /**
37138      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37139      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37140      * (the default format used is "m/d/y").
37141      * <br />Usage:
37142      * <pre><code>
37143 //All of these calls set the same date value (May 4, 2006)
37144
37145 //Pass a date object:
37146 var dt = new Date('5/4/06');
37147 dateField.setValue(dt);
37148
37149 //Pass a date string (default format):
37150 dateField.setValue('5/4/06');
37151
37152 //Pass a date string (custom format):
37153 dateField.format = 'Y-m-d';
37154 dateField.setValue('2006-5-4');
37155 </code></pre>
37156      * @param {String/Date} date The date or valid date string
37157      */
37158     setValue : function(date){
37159         if (this.hiddenField) {
37160             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37161         }
37162         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37163     },
37164
37165     // private
37166     parseDate : function(value){
37167         if(!value || value instanceof Date){
37168             return value;
37169         }
37170         var v = Date.parseDate(value, this.format);
37171         if(!v && this.altFormats){
37172             if(!this.altFormatsArray){
37173                 this.altFormatsArray = this.altFormats.split("|");
37174             }
37175             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37176                 v = Date.parseDate(value, this.altFormatsArray[i]);
37177             }
37178         }
37179         return v;
37180     },
37181
37182     // private
37183     formatDate : function(date, fmt){
37184         return (!date || !(date instanceof Date)) ?
37185                date : date.dateFormat(fmt || this.format);
37186     },
37187
37188     // private
37189     menuListeners : {
37190         select: function(m, d){
37191             this.setValue(d);
37192             this.fireEvent('select', this, d);
37193         },
37194         show : function(){ // retain focus styling
37195             this.onFocus();
37196         },
37197         hide : function(){
37198             this.focus.defer(10, this);
37199             var ml = this.menuListeners;
37200             this.menu.un("select", ml.select,  this);
37201             this.menu.un("show", ml.show,  this);
37202             this.menu.un("hide", ml.hide,  this);
37203         }
37204     },
37205
37206     // private
37207     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37208     onTriggerClick : function(){
37209         if(this.disabled){
37210             return;
37211         }
37212         if(this.menu == null){
37213             this.menu = new Roo.menu.DateMenu();
37214         }
37215         Roo.apply(this.menu.picker,  {
37216             showClear: this.allowBlank,
37217             minDate : this.minValue,
37218             maxDate : this.maxValue,
37219             disabledDatesRE : this.ddMatch,
37220             disabledDatesText : this.disabledDatesText,
37221             disabledDays : this.disabledDays,
37222             disabledDaysText : this.disabledDaysText,
37223             format : this.format,
37224             minText : String.format(this.minText, this.formatDate(this.minValue)),
37225             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37226         });
37227         this.menu.on(Roo.apply({}, this.menuListeners, {
37228             scope:this
37229         }));
37230         this.menu.picker.setValue(this.getValue() || new Date());
37231         this.menu.show(this.el, "tl-bl?");
37232     },
37233
37234     beforeBlur : function(){
37235         var v = this.parseDate(this.getRawValue());
37236         if(v){
37237             this.setValue(v);
37238         }
37239     }
37240
37241     /** @cfg {Boolean} grow @hide */
37242     /** @cfg {Number} growMin @hide */
37243     /** @cfg {Number} growMax @hide */
37244     /**
37245      * @hide
37246      * @method autoSize
37247      */
37248 });/*
37249  * Based on:
37250  * Ext JS Library 1.1.1
37251  * Copyright(c) 2006-2007, Ext JS, LLC.
37252  *
37253  * Originally Released Under LGPL - original licence link has changed is not relivant.
37254  *
37255  * Fork - LGPL
37256  * <script type="text/javascript">
37257  */
37258  
37259
37260 /**
37261  * @class Roo.form.ComboBox
37262  * @extends Roo.form.TriggerField
37263  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37264  * @constructor
37265  * Create a new ComboBox.
37266  * @param {Object} config Configuration options
37267  */
37268 Roo.form.ComboBox = function(config){
37269     Roo.form.ComboBox.superclass.constructor.call(this, config);
37270     this.addEvents({
37271         /**
37272          * @event expand
37273          * Fires when the dropdown list is expanded
37274              * @param {Roo.form.ComboBox} combo This combo box
37275              */
37276         'expand' : true,
37277         /**
37278          * @event collapse
37279          * Fires when the dropdown list is collapsed
37280              * @param {Roo.form.ComboBox} combo This combo box
37281              */
37282         'collapse' : true,
37283         /**
37284          * @event beforeselect
37285          * Fires before a list item is selected. Return false to cancel the selection.
37286              * @param {Roo.form.ComboBox} combo This combo box
37287              * @param {Roo.data.Record} record The data record returned from the underlying store
37288              * @param {Number} index The index of the selected item in the dropdown list
37289              */
37290         'beforeselect' : true,
37291         /**
37292          * @event select
37293          * Fires when a list item is selected
37294              * @param {Roo.form.ComboBox} combo This combo box
37295              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37296              * @param {Number} index The index of the selected item in the dropdown list
37297              */
37298         'select' : true,
37299         /**
37300          * @event beforequery
37301          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37302          * The event object passed has these properties:
37303              * @param {Roo.form.ComboBox} combo This combo box
37304              * @param {String} query The query
37305              * @param {Boolean} forceAll true to force "all" query
37306              * @param {Boolean} cancel true to cancel the query
37307              * @param {Object} e The query event object
37308              */
37309         'beforequery': true,
37310          /**
37311          * @event add
37312          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37313              * @param {Roo.form.ComboBox} combo This combo box
37314              */
37315         'add' : true,
37316         /**
37317          * @event edit
37318          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37319              * @param {Roo.form.ComboBox} combo This combo box
37320              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37321              */
37322         'edit' : true
37323         
37324         
37325     });
37326     if(this.transform){
37327         this.allowDomMove = false;
37328         var s = Roo.getDom(this.transform);
37329         if(!this.hiddenName){
37330             this.hiddenName = s.name;
37331         }
37332         if(!this.store){
37333             this.mode = 'local';
37334             var d = [], opts = s.options;
37335             for(var i = 0, len = opts.length;i < len; i++){
37336                 var o = opts[i];
37337                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37338                 if(o.selected) {
37339                     this.value = value;
37340                 }
37341                 d.push([value, o.text]);
37342             }
37343             this.store = new Roo.data.SimpleStore({
37344                 'id': 0,
37345                 fields: ['value', 'text'],
37346                 data : d
37347             });
37348             this.valueField = 'value';
37349             this.displayField = 'text';
37350         }
37351         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37352         if(!this.lazyRender){
37353             this.target = true;
37354             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37355             s.parentNode.removeChild(s); // remove it
37356             this.render(this.el.parentNode);
37357         }else{
37358             s.parentNode.removeChild(s); // remove it
37359         }
37360
37361     }
37362     if (this.store) {
37363         this.store = Roo.factory(this.store, Roo.data);
37364     }
37365     
37366     this.selectedIndex = -1;
37367     if(this.mode == 'local'){
37368         if(config.queryDelay === undefined){
37369             this.queryDelay = 10;
37370         }
37371         if(config.minChars === undefined){
37372             this.minChars = 0;
37373         }
37374     }
37375 };
37376
37377 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37378     /**
37379      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37380      */
37381     /**
37382      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37383      * rendering into an Roo.Editor, defaults to false)
37384      */
37385     /**
37386      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37387      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37388      */
37389     /**
37390      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37391      */
37392     /**
37393      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37394      * the dropdown list (defaults to undefined, with no header element)
37395      */
37396
37397      /**
37398      * @cfg {String/Roo.Template} tpl The template to use to render the output
37399      */
37400      
37401     // private
37402     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37403     /**
37404      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37405      */
37406     listWidth: undefined,
37407     /**
37408      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37409      * mode = 'remote' or 'text' if mode = 'local')
37410      */
37411     displayField: undefined,
37412     /**
37413      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37414      * mode = 'remote' or 'value' if mode = 'local'). 
37415      * Note: use of a valueField requires the user make a selection
37416      * in order for a value to be mapped.
37417      */
37418     valueField: undefined,
37419     /**
37420      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37421      * field's data value (defaults to the underlying DOM element's name)
37422      */
37423     hiddenName: undefined,
37424     /**
37425      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37426      */
37427     listClass: '',
37428     /**
37429      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37430      */
37431     selectedClass: 'x-combo-selected',
37432     /**
37433      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37434      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37435      * which displays a downward arrow icon).
37436      */
37437     triggerClass : 'x-form-arrow-trigger',
37438     /**
37439      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37440      */
37441     shadow:'sides',
37442     /**
37443      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37444      * anchor positions (defaults to 'tl-bl')
37445      */
37446     listAlign: 'tl-bl?',
37447     /**
37448      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37449      */
37450     maxHeight: 300,
37451     /**
37452      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37453      * query specified by the allQuery config option (defaults to 'query')
37454      */
37455     triggerAction: 'query',
37456     /**
37457      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37458      * (defaults to 4, does not apply if editable = false)
37459      */
37460     minChars : 4,
37461     /**
37462      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37463      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37464      */
37465     typeAhead: false,
37466     /**
37467      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37468      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37469      */
37470     queryDelay: 500,
37471     /**
37472      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37473      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37474      */
37475     pageSize: 0,
37476     /**
37477      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37478      * when editable = true (defaults to false)
37479      */
37480     selectOnFocus:false,
37481     /**
37482      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37483      */
37484     queryParam: 'query',
37485     /**
37486      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37487      * when mode = 'remote' (defaults to 'Loading...')
37488      */
37489     loadingText: 'Loading...',
37490     /**
37491      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37492      */
37493     resizable: false,
37494     /**
37495      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37496      */
37497     handleHeight : 8,
37498     /**
37499      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37500      * traditional select (defaults to true)
37501      */
37502     editable: true,
37503     /**
37504      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37505      */
37506     allQuery: '',
37507     /**
37508      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37509      */
37510     mode: 'remote',
37511     /**
37512      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37513      * listWidth has a higher value)
37514      */
37515     minListWidth : 70,
37516     /**
37517      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37518      * allow the user to set arbitrary text into the field (defaults to false)
37519      */
37520     forceSelection:false,
37521     /**
37522      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37523      * if typeAhead = true (defaults to 250)
37524      */
37525     typeAheadDelay : 250,
37526     /**
37527      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37528      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37529      */
37530     valueNotFoundText : undefined,
37531     /**
37532      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37533      */
37534     blockFocus : false,
37535     
37536     /**
37537      * @cfg {Boolean} disableClear Disable showing of clear button.
37538      */
37539     disableClear : false,
37540     /**
37541      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37542      */
37543     alwaysQuery : false,
37544     
37545     //private
37546     addicon : false,
37547     editicon: false,
37548     
37549     
37550     // private
37551     onRender : function(ct, position){
37552         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37553         if(this.hiddenName){
37554             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37555                     'before', true);
37556             this.hiddenField.value =
37557                 this.hiddenValue !== undefined ? this.hiddenValue :
37558                 this.value !== undefined ? this.value : '';
37559
37560             // prevent input submission
37561             this.el.dom.removeAttribute('name');
37562         }
37563         if(Roo.isGecko){
37564             this.el.dom.setAttribute('autocomplete', 'off');
37565         }
37566
37567         var cls = 'x-combo-list';
37568
37569         this.list = new Roo.Layer({
37570             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37571         });
37572
37573         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37574         this.list.setWidth(lw);
37575         this.list.swallowEvent('mousewheel');
37576         this.assetHeight = 0;
37577
37578         if(this.title){
37579             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37580             this.assetHeight += this.header.getHeight();
37581         }
37582
37583         this.innerList = this.list.createChild({cls:cls+'-inner'});
37584         this.innerList.on('mouseover', this.onViewOver, this);
37585         this.innerList.on('mousemove', this.onViewMove, this);
37586         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37587         
37588         if(this.allowBlank && !this.pageSize && !this.disableClear){
37589             this.footer = this.list.createChild({cls:cls+'-ft'});
37590             this.pageTb = new Roo.Toolbar(this.footer);
37591            
37592         }
37593         if(this.pageSize){
37594             this.footer = this.list.createChild({cls:cls+'-ft'});
37595             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37596                     {pageSize: this.pageSize});
37597             
37598         }
37599         
37600         if (this.pageTb && this.allowBlank && !this.disableClear) {
37601             var _this = this;
37602             this.pageTb.add(new Roo.Toolbar.Fill(), {
37603                 cls: 'x-btn-icon x-btn-clear',
37604                 text: '&#160;',
37605                 handler: function()
37606                 {
37607                     _this.collapse();
37608                     _this.clearValue();
37609                     _this.onSelect(false, -1);
37610                 }
37611             });
37612         }
37613         if (this.footer) {
37614             this.assetHeight += this.footer.getHeight();
37615         }
37616         
37617
37618         if(!this.tpl){
37619             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37620         }
37621
37622         this.view = new Roo.View(this.innerList, this.tpl, {
37623             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37624         });
37625
37626         this.view.on('click', this.onViewClick, this);
37627
37628         this.store.on('beforeload', this.onBeforeLoad, this);
37629         this.store.on('load', this.onLoad, this);
37630         this.store.on('loadexception', this.collapse, this);
37631
37632         if(this.resizable){
37633             this.resizer = new Roo.Resizable(this.list,  {
37634                pinned:true, handles:'se'
37635             });
37636             this.resizer.on('resize', function(r, w, h){
37637                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37638                 this.listWidth = w;
37639                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37640                 this.restrictHeight();
37641             }, this);
37642             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37643         }
37644         if(!this.editable){
37645             this.editable = true;
37646             this.setEditable(false);
37647         }  
37648         
37649         
37650         if (typeof(this.events.add.listeners) != 'undefined') {
37651             
37652             this.addicon = this.wrap.createChild(
37653                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37654        
37655             this.addicon.on('click', function(e) {
37656                 this.fireEvent('add', this);
37657             }, this);
37658         }
37659         if (typeof(this.events.edit.listeners) != 'undefined') {
37660             
37661             this.editicon = this.wrap.createChild(
37662                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37663             if (this.addicon) {
37664                 this.editicon.setStyle('margin-left', '40px');
37665             }
37666             this.editicon.on('click', function(e) {
37667                 
37668                 // we fire even  if inothing is selected..
37669                 this.fireEvent('edit', this, this.lastData );
37670                 
37671             }, this);
37672         }
37673         
37674         
37675         
37676     },
37677
37678     // private
37679     initEvents : function(){
37680         Roo.form.ComboBox.superclass.initEvents.call(this);
37681
37682         this.keyNav = new Roo.KeyNav(this.el, {
37683             "up" : function(e){
37684                 this.inKeyMode = true;
37685                 this.selectPrev();
37686             },
37687
37688             "down" : function(e){
37689                 if(!this.isExpanded()){
37690                     this.onTriggerClick();
37691                 }else{
37692                     this.inKeyMode = true;
37693                     this.selectNext();
37694                 }
37695             },
37696
37697             "enter" : function(e){
37698                 this.onViewClick();
37699                 //return true;
37700             },
37701
37702             "esc" : function(e){
37703                 this.collapse();
37704             },
37705
37706             "tab" : function(e){
37707                 this.onViewClick(false);
37708                 this.fireEvent("specialkey", this, e);
37709                 return true;
37710             },
37711
37712             scope : this,
37713
37714             doRelay : function(foo, bar, hname){
37715                 if(hname == 'down' || this.scope.isExpanded()){
37716                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37717                 }
37718                 return true;
37719             },
37720
37721             forceKeyDown: true
37722         });
37723         this.queryDelay = Math.max(this.queryDelay || 10,
37724                 this.mode == 'local' ? 10 : 250);
37725         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37726         if(this.typeAhead){
37727             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37728         }
37729         if(this.editable !== false){
37730             this.el.on("keyup", this.onKeyUp, this);
37731         }
37732         if(this.forceSelection){
37733             this.on('blur', this.doForce, this);
37734         }
37735     },
37736
37737     onDestroy : function(){
37738         if(this.view){
37739             this.view.setStore(null);
37740             this.view.el.removeAllListeners();
37741             this.view.el.remove();
37742             this.view.purgeListeners();
37743         }
37744         if(this.list){
37745             this.list.destroy();
37746         }
37747         if(this.store){
37748             this.store.un('beforeload', this.onBeforeLoad, this);
37749             this.store.un('load', this.onLoad, this);
37750             this.store.un('loadexception', this.collapse, this);
37751         }
37752         Roo.form.ComboBox.superclass.onDestroy.call(this);
37753     },
37754
37755     // private
37756     fireKey : function(e){
37757         if(e.isNavKeyPress() && !this.list.isVisible()){
37758             this.fireEvent("specialkey", this, e);
37759         }
37760     },
37761
37762     // private
37763     onResize: function(w, h){
37764         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37765         
37766         if(typeof w != 'number'){
37767             // we do not handle it!?!?
37768             return;
37769         }
37770         var tw = this.trigger.getWidth();
37771         tw += this.addicon ? this.addicon.getWidth() : 0;
37772         tw += this.editicon ? this.editicon.getWidth() : 0;
37773         var x = w - tw;
37774         this.el.setWidth( this.adjustWidth('input', x));
37775             
37776         this.trigger.setStyle('left', x+'px');
37777         
37778         if(this.list && this.listWidth === undefined){
37779             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37780             this.list.setWidth(lw);
37781             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37782         }
37783         
37784     
37785         
37786     },
37787
37788     /**
37789      * Allow or prevent the user from directly editing the field text.  If false is passed,
37790      * the user will only be able to select from the items defined in the dropdown list.  This method
37791      * is the runtime equivalent of setting the 'editable' config option at config time.
37792      * @param {Boolean} value True to allow the user to directly edit the field text
37793      */
37794     setEditable : function(value){
37795         if(value == this.editable){
37796             return;
37797         }
37798         this.editable = value;
37799         if(!value){
37800             this.el.dom.setAttribute('readOnly', true);
37801             this.el.on('mousedown', this.onTriggerClick,  this);
37802             this.el.addClass('x-combo-noedit');
37803         }else{
37804             this.el.dom.setAttribute('readOnly', false);
37805             this.el.un('mousedown', this.onTriggerClick,  this);
37806             this.el.removeClass('x-combo-noedit');
37807         }
37808     },
37809
37810     // private
37811     onBeforeLoad : function(){
37812         if(!this.hasFocus){
37813             return;
37814         }
37815         this.innerList.update(this.loadingText ?
37816                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37817         this.restrictHeight();
37818         this.selectedIndex = -1;
37819     },
37820
37821     // private
37822     onLoad : function(){
37823         if(!this.hasFocus){
37824             return;
37825         }
37826         if(this.store.getCount() > 0){
37827             this.expand();
37828             this.restrictHeight();
37829             if(this.lastQuery == this.allQuery){
37830                 if(this.editable){
37831                     this.el.dom.select();
37832                 }
37833                 if(!this.selectByValue(this.value, true)){
37834                     this.select(0, true);
37835                 }
37836             }else{
37837                 this.selectNext();
37838                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37839                     this.taTask.delay(this.typeAheadDelay);
37840                 }
37841             }
37842         }else{
37843             this.onEmptyResults();
37844         }
37845         //this.el.focus();
37846     },
37847
37848     // private
37849     onTypeAhead : function(){
37850         if(this.store.getCount() > 0){
37851             var r = this.store.getAt(0);
37852             var newValue = r.data[this.displayField];
37853             var len = newValue.length;
37854             var selStart = this.getRawValue().length;
37855             if(selStart != len){
37856                 this.setRawValue(newValue);
37857                 this.selectText(selStart, newValue.length);
37858             }
37859         }
37860     },
37861
37862     // private
37863     onSelect : function(record, index){
37864         if(this.fireEvent('beforeselect', this, record, index) !== false){
37865             this.setFromData(index > -1 ? record.data : false);
37866             this.collapse();
37867             this.fireEvent('select', this, record, index);
37868         }
37869     },
37870
37871     /**
37872      * Returns the currently selected field value or empty string if no value is set.
37873      * @return {String} value The selected value
37874      */
37875     getValue : function(){
37876         if(this.valueField){
37877             return typeof this.value != 'undefined' ? this.value : '';
37878         }else{
37879             return Roo.form.ComboBox.superclass.getValue.call(this);
37880         }
37881     },
37882
37883     /**
37884      * Clears any text/value currently set in the field
37885      */
37886     clearValue : function(){
37887         if(this.hiddenField){
37888             this.hiddenField.value = '';
37889         }
37890         this.value = '';
37891         this.setRawValue('');
37892         this.lastSelectionText = '';
37893         this.applyEmptyText();
37894     },
37895
37896     /**
37897      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37898      * will be displayed in the field.  If the value does not match the data value of an existing item,
37899      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37900      * Otherwise the field will be blank (although the value will still be set).
37901      * @param {String} value The value to match
37902      */
37903     setValue : function(v){
37904         var text = v;
37905         if(this.valueField){
37906             var r = this.findRecord(this.valueField, v);
37907             if(r){
37908                 text = r.data[this.displayField];
37909             }else if(this.valueNotFoundText !== undefined){
37910                 text = this.valueNotFoundText;
37911             }
37912         }
37913         this.lastSelectionText = text;
37914         if(this.hiddenField){
37915             this.hiddenField.value = v;
37916         }
37917         Roo.form.ComboBox.superclass.setValue.call(this, text);
37918         this.value = v;
37919     },
37920     /**
37921      * @property {Object} the last set data for the element
37922      */
37923     
37924     lastData : false,
37925     /**
37926      * Sets the value of the field based on a object which is related to the record format for the store.
37927      * @param {Object} value the value to set as. or false on reset?
37928      */
37929     setFromData : function(o){
37930         var dv = ''; // display value
37931         var vv = ''; // value value..
37932         this.lastData = o;
37933         if (this.displayField) {
37934             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37935         } else {
37936             // this is an error condition!!!
37937             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37938         }
37939         
37940         if(this.valueField){
37941             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37942         }
37943         if(this.hiddenField){
37944             this.hiddenField.value = vv;
37945             
37946             this.lastSelectionText = dv;
37947             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37948             this.value = vv;
37949             return;
37950         }
37951         // no hidden field.. - we store the value in 'value', but still display
37952         // display field!!!!
37953         this.lastSelectionText = dv;
37954         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37955         this.value = vv;
37956         
37957         
37958     },
37959     // private
37960     reset : function(){
37961         // overridden so that last data is reset..
37962         this.setValue(this.originalValue);
37963         this.clearInvalid();
37964         this.lastData = false;
37965     },
37966     // private
37967     findRecord : function(prop, value){
37968         var record;
37969         if(this.store.getCount() > 0){
37970             this.store.each(function(r){
37971                 if(r.data[prop] == value){
37972                     record = r;
37973                     return false;
37974                 }
37975             });
37976         }
37977         return record;
37978     },
37979
37980     // private
37981     onViewMove : function(e, t){
37982         this.inKeyMode = false;
37983     },
37984
37985     // private
37986     onViewOver : function(e, t){
37987         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37988             return;
37989         }
37990         var item = this.view.findItemFromChild(t);
37991         if(item){
37992             var index = this.view.indexOf(item);
37993             this.select(index, false);
37994         }
37995     },
37996
37997     // private
37998     onViewClick : function(doFocus)
37999     {
38000         var index = this.view.getSelectedIndexes()[0];
38001         var r = this.store.getAt(index);
38002         if(r){
38003             this.onSelect(r, index);
38004         }
38005         if(doFocus !== false && !this.blockFocus){
38006             this.el.focus();
38007         }
38008     },
38009
38010     // private
38011     restrictHeight : function(){
38012         this.innerList.dom.style.height = '';
38013         var inner = this.innerList.dom;
38014         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38015         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38016         this.list.beginUpdate();
38017         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38018         this.list.alignTo(this.el, this.listAlign);
38019         this.list.endUpdate();
38020     },
38021
38022     // private
38023     onEmptyResults : function(){
38024         this.collapse();
38025     },
38026
38027     /**
38028      * Returns true if the dropdown list is expanded, else false.
38029      */
38030     isExpanded : function(){
38031         return this.list.isVisible();
38032     },
38033
38034     /**
38035      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38036      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38037      * @param {String} value The data value of the item to select
38038      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38039      * selected item if it is not currently in view (defaults to true)
38040      * @return {Boolean} True if the value matched an item in the list, else false
38041      */
38042     selectByValue : function(v, scrollIntoView){
38043         if(v !== undefined && v !== null){
38044             var r = this.findRecord(this.valueField || this.displayField, v);
38045             if(r){
38046                 this.select(this.store.indexOf(r), scrollIntoView);
38047                 return true;
38048             }
38049         }
38050         return false;
38051     },
38052
38053     /**
38054      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38055      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38056      * @param {Number} index The zero-based index of the list item to select
38057      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38058      * selected item if it is not currently in view (defaults to true)
38059      */
38060     select : function(index, scrollIntoView){
38061         this.selectedIndex = index;
38062         this.view.select(index);
38063         if(scrollIntoView !== false){
38064             var el = this.view.getNode(index);
38065             if(el){
38066                 this.innerList.scrollChildIntoView(el, false);
38067             }
38068         }
38069     },
38070
38071     // private
38072     selectNext : function(){
38073         var ct = this.store.getCount();
38074         if(ct > 0){
38075             if(this.selectedIndex == -1){
38076                 this.select(0);
38077             }else if(this.selectedIndex < ct-1){
38078                 this.select(this.selectedIndex+1);
38079             }
38080         }
38081     },
38082
38083     // private
38084     selectPrev : function(){
38085         var ct = this.store.getCount();
38086         if(ct > 0){
38087             if(this.selectedIndex == -1){
38088                 this.select(0);
38089             }else if(this.selectedIndex != 0){
38090                 this.select(this.selectedIndex-1);
38091             }
38092         }
38093     },
38094
38095     // private
38096     onKeyUp : function(e){
38097         if(this.editable !== false && !e.isSpecialKey()){
38098             this.lastKey = e.getKey();
38099             this.dqTask.delay(this.queryDelay);
38100         }
38101     },
38102
38103     // private
38104     validateBlur : function(){
38105         return !this.list || !this.list.isVisible();   
38106     },
38107
38108     // private
38109     initQuery : function(){
38110         this.doQuery(this.getRawValue());
38111     },
38112
38113     // private
38114     doForce : function(){
38115         if(this.el.dom.value.length > 0){
38116             this.el.dom.value =
38117                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38118             this.applyEmptyText();
38119         }
38120     },
38121
38122     /**
38123      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38124      * query allowing the query action to be canceled if needed.
38125      * @param {String} query The SQL query to execute
38126      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38127      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38128      * saved in the current store (defaults to false)
38129      */
38130     doQuery : function(q, forceAll){
38131         if(q === undefined || q === null){
38132             q = '';
38133         }
38134         var qe = {
38135             query: q,
38136             forceAll: forceAll,
38137             combo: this,
38138             cancel:false
38139         };
38140         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38141             return false;
38142         }
38143         q = qe.query;
38144         forceAll = qe.forceAll;
38145         if(forceAll === true || (q.length >= this.minChars)){
38146             if(this.lastQuery != q || this.alwaysQuery){
38147                 this.lastQuery = q;
38148                 if(this.mode == 'local'){
38149                     this.selectedIndex = -1;
38150                     if(forceAll){
38151                         this.store.clearFilter();
38152                     }else{
38153                         this.store.filter(this.displayField, q);
38154                     }
38155                     this.onLoad();
38156                 }else{
38157                     this.store.baseParams[this.queryParam] = q;
38158                     this.store.load({
38159                         params: this.getParams(q)
38160                     });
38161                     this.expand();
38162                 }
38163             }else{
38164                 this.selectedIndex = -1;
38165                 this.onLoad();   
38166             }
38167         }
38168     },
38169
38170     // private
38171     getParams : function(q){
38172         var p = {};
38173         //p[this.queryParam] = q;
38174         if(this.pageSize){
38175             p.start = 0;
38176             p.limit = this.pageSize;
38177         }
38178         return p;
38179     },
38180
38181     /**
38182      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38183      */
38184     collapse : function(){
38185         if(!this.isExpanded()){
38186             return;
38187         }
38188         this.list.hide();
38189         Roo.get(document).un('mousedown', this.collapseIf, this);
38190         Roo.get(document).un('mousewheel', this.collapseIf, this);
38191         if (!this.editable) {
38192             Roo.get(document).un('keydown', this.listKeyPress, this);
38193         }
38194         this.fireEvent('collapse', this);
38195     },
38196
38197     // private
38198     collapseIf : function(e){
38199         if(!e.within(this.wrap) && !e.within(this.list)){
38200             this.collapse();
38201         }
38202     },
38203
38204     /**
38205      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38206      */
38207     expand : function(){
38208         if(this.isExpanded() || !this.hasFocus){
38209             return;
38210         }
38211         this.list.alignTo(this.el, this.listAlign);
38212         this.list.show();
38213         Roo.get(document).on('mousedown', this.collapseIf, this);
38214         Roo.get(document).on('mousewheel', this.collapseIf, this);
38215         if (!this.editable) {
38216             Roo.get(document).on('keydown', this.listKeyPress, this);
38217         }
38218         
38219         this.fireEvent('expand', this);
38220     },
38221
38222     // private
38223     // Implements the default empty TriggerField.onTriggerClick function
38224     onTriggerClick : function(){
38225         if(this.disabled){
38226             return;
38227         }
38228         if(this.isExpanded()){
38229             this.collapse();
38230             if (!this.blockFocus) {
38231                 this.el.focus();
38232             }
38233             
38234         }else {
38235             this.hasFocus = true;
38236             if(this.triggerAction == 'all') {
38237                 this.doQuery(this.allQuery, true);
38238             } else {
38239                 this.doQuery(this.getRawValue());
38240             }
38241             if (!this.blockFocus) {
38242                 this.el.focus();
38243             }
38244         }
38245     },
38246     listKeyPress : function(e)
38247     {
38248         //Roo.log('listkeypress');
38249         // scroll to first matching element based on key pres..
38250         if (e.isSpecialKey()) {
38251             return false;
38252         }
38253         var k = String.fromCharCode(e.getKey()).toUpperCase();
38254         //Roo.log(k);
38255         var match  = false;
38256         var csel = this.view.getSelectedNodes();
38257         var cselitem = false;
38258         if (csel.length) {
38259             var ix = this.view.indexOf(csel[0]);
38260             cselitem  = this.store.getAt(ix);
38261             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38262                 cselitem = false;
38263             }
38264             
38265         }
38266         
38267         this.store.each(function(v) { 
38268             if (cselitem) {
38269                 // start at existing selection.
38270                 if (cselitem.id == v.id) {
38271                     cselitem = false;
38272                 }
38273                 return;
38274             }
38275                 
38276             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38277                 match = this.store.indexOf(v);
38278                 return false;
38279             }
38280         }, this);
38281         
38282         if (match === false) {
38283             return true; // no more action?
38284         }
38285         // scroll to?
38286         this.view.select(match);
38287         var sn = Roo.get(this.view.getSelectedNodes()[0])
38288         sn.scrollIntoView(sn.dom.parentNode, false);
38289     }
38290
38291     /** 
38292     * @cfg {Boolean} grow 
38293     * @hide 
38294     */
38295     /** 
38296     * @cfg {Number} growMin 
38297     * @hide 
38298     */
38299     /** 
38300     * @cfg {Number} growMax 
38301     * @hide 
38302     */
38303     /**
38304      * @hide
38305      * @method autoSize
38306      */
38307 });/*
38308  * Based on:
38309  * Ext JS Library 1.1.1
38310  * Copyright(c) 2006-2007, Ext JS, LLC.
38311  *
38312  * Originally Released Under LGPL - original licence link has changed is not relivant.
38313  *
38314  * Fork - LGPL
38315  * <script type="text/javascript">
38316  */
38317 /**
38318  * @class Roo.form.Checkbox
38319  * @extends Roo.form.Field
38320  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38321  * @constructor
38322  * Creates a new Checkbox
38323  * @param {Object} config Configuration options
38324  */
38325 Roo.form.Checkbox = function(config){
38326     Roo.form.Checkbox.superclass.constructor.call(this, config);
38327     this.addEvents({
38328         /**
38329          * @event check
38330          * Fires when the checkbox is checked or unchecked.
38331              * @param {Roo.form.Checkbox} this This checkbox
38332              * @param {Boolean} checked The new checked value
38333              */
38334         check : true
38335     });
38336 };
38337
38338 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38339     /**
38340      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38341      */
38342     focusClass : undefined,
38343     /**
38344      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38345      */
38346     fieldClass: "x-form-field",
38347     /**
38348      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38349      */
38350     checked: false,
38351     /**
38352      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38353      * {tag: "input", type: "checkbox", autocomplete: "off"})
38354      */
38355     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38356     /**
38357      * @cfg {String} boxLabel The text that appears beside the checkbox
38358      */
38359     boxLabel : "",
38360     /**
38361      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38362      */  
38363     inputValue : '1',
38364     /**
38365      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38366      */
38367      valueOff: '0', // value when not checked..
38368
38369     actionMode : 'viewEl', 
38370     //
38371     // private
38372     itemCls : 'x-menu-check-item x-form-item',
38373     groupClass : 'x-menu-group-item',
38374     inputType : 'hidden',
38375     
38376     
38377     inSetChecked: false, // check that we are not calling self...
38378     
38379     inputElement: false, // real input element?
38380     basedOn: false, // ????
38381     
38382     isFormField: true, // not sure where this is needed!!!!
38383
38384     onResize : function(){
38385         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38386         if(!this.boxLabel){
38387             this.el.alignTo(this.wrap, 'c-c');
38388         }
38389     },
38390
38391     initEvents : function(){
38392         Roo.form.Checkbox.superclass.initEvents.call(this);
38393         this.el.on("click", this.onClick,  this);
38394         this.el.on("change", this.onClick,  this);
38395     },
38396
38397
38398     getResizeEl : function(){
38399         return this.wrap;
38400     },
38401
38402     getPositionEl : function(){
38403         return this.wrap;
38404     },
38405
38406     // private
38407     onRender : function(ct, position){
38408         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38409         /*
38410         if(this.inputValue !== undefined){
38411             this.el.dom.value = this.inputValue;
38412         }
38413         */
38414         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38415         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38416         var viewEl = this.wrap.createChild({ 
38417             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38418         this.viewEl = viewEl;   
38419         this.wrap.on('click', this.onClick,  this); 
38420         
38421         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38422         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38423         
38424         
38425         
38426         if(this.boxLabel){
38427             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38428         //    viewEl.on('click', this.onClick,  this); 
38429         }
38430         //if(this.checked){
38431             this.setChecked(this.checked);
38432         //}else{
38433             //this.checked = this.el.dom;
38434         //}
38435
38436     },
38437
38438     // private
38439     initValue : Roo.emptyFn,
38440
38441     /**
38442      * Returns the checked state of the checkbox.
38443      * @return {Boolean} True if checked, else false
38444      */
38445     getValue : function(){
38446         if(this.el){
38447             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38448         }
38449         return this.valueOff;
38450         
38451     },
38452
38453         // private
38454     onClick : function(){ 
38455         this.setChecked(!this.checked);
38456
38457         //if(this.el.dom.checked != this.checked){
38458         //    this.setValue(this.el.dom.checked);
38459        // }
38460     },
38461
38462     /**
38463      * Sets the checked state of the checkbox.
38464      * On is always based on a string comparison between inputValue and the param.
38465      * @param {Boolean/String} value - the value to set 
38466      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38467      */
38468     setValue : function(v,suppressEvent){
38469         
38470         
38471         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38472         //if(this.el && this.el.dom){
38473         //    this.el.dom.checked = this.checked;
38474         //    this.el.dom.defaultChecked = this.checked;
38475         //}
38476         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38477         //this.fireEvent("check", this, this.checked);
38478     },
38479     // private..
38480     setChecked : function(state,suppressEvent)
38481     {
38482         if (this.inSetChecked) {
38483             this.checked = state;
38484             return;
38485         }
38486         
38487     
38488         if(this.wrap){
38489             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38490         }
38491         this.checked = state;
38492         if(suppressEvent !== true){
38493             this.fireEvent('check', this, state);
38494         }
38495         this.inSetChecked = true;
38496         this.el.dom.value = state ? this.inputValue : this.valueOff;
38497         this.inSetChecked = false;
38498         
38499     },
38500     // handle setting of hidden value by some other method!!?!?
38501     setFromHidden: function()
38502     {
38503         if(!this.el){
38504             return;
38505         }
38506         //console.log("SET FROM HIDDEN");
38507         //alert('setFrom hidden');
38508         this.setValue(this.el.dom.value);
38509     },
38510     
38511     onDestroy : function()
38512     {
38513         if(this.viewEl){
38514             Roo.get(this.viewEl).remove();
38515         }
38516          
38517         Roo.form.Checkbox.superclass.onDestroy.call(this);
38518     }
38519
38520 });/*
38521  * Based on:
38522  * Ext JS Library 1.1.1
38523  * Copyright(c) 2006-2007, Ext JS, LLC.
38524  *
38525  * Originally Released Under LGPL - original licence link has changed is not relivant.
38526  *
38527  * Fork - LGPL
38528  * <script type="text/javascript">
38529  */
38530  
38531 /**
38532  * @class Roo.form.Radio
38533  * @extends Roo.form.Checkbox
38534  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38535  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38536  * @constructor
38537  * Creates a new Radio
38538  * @param {Object} config Configuration options
38539  */
38540 Roo.form.Radio = function(){
38541     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38542 };
38543 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38544     inputType: 'radio',
38545
38546     /**
38547      * If this radio is part of a group, it will return the selected value
38548      * @return {String}
38549      */
38550     getGroupValue : function(){
38551         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38552     }
38553 });//<script type="text/javascript">
38554
38555 /*
38556  * Ext JS Library 1.1.1
38557  * Copyright(c) 2006-2007, Ext JS, LLC.
38558  * licensing@extjs.com
38559  * 
38560  * http://www.extjs.com/license
38561  */
38562  
38563  /*
38564   * 
38565   * Known bugs:
38566   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38567   * - IE ? - no idea how much works there.
38568   * 
38569   * 
38570   * 
38571   */
38572  
38573
38574 /**
38575  * @class Ext.form.HtmlEditor
38576  * @extends Ext.form.Field
38577  * Provides a lightweight HTML Editor component.
38578  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38579  * 
38580  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38581  * supported by this editor.</b><br/><br/>
38582  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38583  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38584  */
38585 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38586       /**
38587      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38588      */
38589     toolbars : false,
38590     /**
38591      * @cfg {String} createLinkText The default text for the create link prompt
38592      */
38593     createLinkText : 'Please enter the URL for the link:',
38594     /**
38595      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38596      */
38597     defaultLinkValue : 'http:/'+'/',
38598    
38599      /**
38600      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38601      *                        Roo.resizable.
38602      */
38603     resizable : false,
38604      /**
38605      * @cfg {Number} height (in pixels)
38606      */   
38607     height: 300,
38608    /**
38609      * @cfg {Number} width (in pixels)
38610      */   
38611     width: 500,
38612     
38613     /**
38614      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38615      * 
38616      */
38617     stylesheets: false,
38618     
38619     // id of frame..
38620     frameId: false,
38621     
38622     // private properties
38623     validationEvent : false,
38624     deferHeight: true,
38625     initialized : false,
38626     activated : false,
38627     sourceEditMode : false,
38628     onFocus : Roo.emptyFn,
38629     iframePad:3,
38630     hideMode:'offsets',
38631     
38632     defaultAutoCreate : { // modified by initCompnoent..
38633         tag: "textarea",
38634         style:"width:500px;height:300px;",
38635         autocomplete: "off"
38636     },
38637
38638     // private
38639     initComponent : function(){
38640         this.addEvents({
38641             /**
38642              * @event initialize
38643              * Fires when the editor is fully initialized (including the iframe)
38644              * @param {HtmlEditor} this
38645              */
38646             initialize: true,
38647             /**
38648              * @event activate
38649              * Fires when the editor is first receives the focus. Any insertion must wait
38650              * until after this event.
38651              * @param {HtmlEditor} this
38652              */
38653             activate: true,
38654              /**
38655              * @event beforesync
38656              * Fires before the textarea is updated with content from the editor iframe. Return false
38657              * to cancel the sync.
38658              * @param {HtmlEditor} this
38659              * @param {String} html
38660              */
38661             beforesync: true,
38662              /**
38663              * @event beforepush
38664              * Fires before the iframe editor is updated with content from the textarea. Return false
38665              * to cancel the push.
38666              * @param {HtmlEditor} this
38667              * @param {String} html
38668              */
38669             beforepush: true,
38670              /**
38671              * @event sync
38672              * Fires when the textarea is updated with content from the editor iframe.
38673              * @param {HtmlEditor} this
38674              * @param {String} html
38675              */
38676             sync: true,
38677              /**
38678              * @event push
38679              * Fires when the iframe editor is updated with content from the textarea.
38680              * @param {HtmlEditor} this
38681              * @param {String} html
38682              */
38683             push: true,
38684              /**
38685              * @event editmodechange
38686              * Fires when the editor switches edit modes
38687              * @param {HtmlEditor} this
38688              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38689              */
38690             editmodechange: true,
38691             /**
38692              * @event editorevent
38693              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38694              * @param {HtmlEditor} this
38695              */
38696             editorevent: true
38697         });
38698         this.defaultAutoCreate =  {
38699             tag: "textarea",
38700             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38701             autocomplete: "off"
38702         };
38703     },
38704
38705     /**
38706      * Protected method that will not generally be called directly. It
38707      * is called when the editor creates its toolbar. Override this method if you need to
38708      * add custom toolbar buttons.
38709      * @param {HtmlEditor} editor
38710      */
38711     createToolbar : function(editor){
38712         if (!editor.toolbars || !editor.toolbars.length) {
38713             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38714         }
38715         
38716         for (var i =0 ; i < editor.toolbars.length;i++) {
38717             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38718             editor.toolbars[i].init(editor);
38719         }
38720          
38721         
38722     },
38723
38724     /**
38725      * Protected method that will not generally be called directly. It
38726      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38727      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38728      */
38729     getDocMarkup : function(){
38730         // body styles..
38731         var st = '';
38732         if (this.stylesheets === false) {
38733             
38734             Roo.get(document.head).select('style').each(function(node) {
38735                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38736             });
38737             
38738             Roo.get(document.head).select('link').each(function(node) { 
38739                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38740             });
38741             
38742         } else if (!this.stylesheets.length) {
38743                 // simple..
38744                 st = '<style type="text/css">' +
38745                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38746                    '</style>';
38747         } else {
38748             Roo.each(this.stylesheets, function(s) {
38749                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38750             });
38751             
38752         }
38753         
38754         return '<html><head>' + st  +
38755             //<style type="text/css">' +
38756             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38757             //'</style>' +
38758             ' </head><body></body></html>';
38759     },
38760
38761     // private
38762     onRender : function(ct, position)
38763     {
38764         var _t = this;
38765         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38766         this.el.dom.style.border = '0 none';
38767         this.el.dom.setAttribute('tabIndex', -1);
38768         this.el.addClass('x-hidden');
38769         if(Roo.isIE){ // fix IE 1px bogus margin
38770             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38771         }
38772         this.wrap = this.el.wrap({
38773             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38774         });
38775         
38776         if (this.resizable) {
38777             this.resizeEl = new Roo.Resizable(this.wrap, {
38778                 pinned : true,
38779                 wrap: true,
38780                 dynamic : true,
38781                 minHeight : this.height,
38782                 height: this.height,
38783                 handles : this.resizable,
38784                 width: this.width,
38785                 listeners : {
38786                     resize : function(r, w, h) {
38787                         _t.onResize(w,h); // -something
38788                     }
38789                 }
38790             });
38791             
38792         }
38793
38794         this.frameId = Roo.id();
38795         
38796         this.createToolbar(this);
38797         
38798       
38799         
38800         var iframe = this.wrap.createChild({
38801             tag: 'iframe',
38802             id: this.frameId,
38803             name: this.frameId,
38804             frameBorder : 'no',
38805             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38806         }, this.el
38807         );
38808         
38809        // console.log(iframe);
38810         //this.wrap.dom.appendChild(iframe);
38811
38812         this.iframe = iframe.dom;
38813
38814          this.assignDocWin();
38815         
38816         this.doc.designMode = 'on';
38817        
38818         this.doc.open();
38819         this.doc.write(this.getDocMarkup());
38820         this.doc.close();
38821
38822         
38823         var task = { // must defer to wait for browser to be ready
38824             run : function(){
38825                 //console.log("run task?" + this.doc.readyState);
38826                 this.assignDocWin();
38827                 if(this.doc.body || this.doc.readyState == 'complete'){
38828                     try {
38829                         this.doc.designMode="on";
38830                     } catch (e) {
38831                         return;
38832                     }
38833                     Roo.TaskMgr.stop(task);
38834                     this.initEditor.defer(10, this);
38835                 }
38836             },
38837             interval : 10,
38838             duration:10000,
38839             scope: this
38840         };
38841         Roo.TaskMgr.start(task);
38842
38843         if(!this.width){
38844             this.setSize(this.wrap.getSize());
38845         }
38846         if (this.resizeEl) {
38847             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38848             // should trigger onReize..
38849         }
38850     },
38851
38852     // private
38853     onResize : function(w, h)
38854     {
38855         //Roo.log('resize: ' +w + ',' + h );
38856         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38857         if(this.el && this.iframe){
38858             if(typeof w == 'number'){
38859                 var aw = w - this.wrap.getFrameWidth('lr');
38860                 this.el.setWidth(this.adjustWidth('textarea', aw));
38861                 this.iframe.style.width = aw + 'px';
38862             }
38863             if(typeof h == 'number'){
38864                 var tbh = 0;
38865                 for (var i =0; i < this.toolbars.length;i++) {
38866                     // fixme - ask toolbars for heights?
38867                     tbh += this.toolbars[i].tb.el.getHeight();
38868                     if (this.toolbars[i].footer) {
38869                         tbh += this.toolbars[i].footer.el.getHeight();
38870                     }
38871                 }
38872                 
38873                 
38874                 
38875                 
38876                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38877                 ah -= 5; // knock a few pixes off for look..
38878                 this.el.setHeight(this.adjustWidth('textarea', ah));
38879                 this.iframe.style.height = ah + 'px';
38880                 if(this.doc){
38881                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38882                 }
38883             }
38884         }
38885     },
38886
38887     /**
38888      * Toggles the editor between standard and source edit mode.
38889      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38890      */
38891     toggleSourceEdit : function(sourceEditMode){
38892         
38893         this.sourceEditMode = sourceEditMode === true;
38894         
38895         if(this.sourceEditMode){
38896           
38897             this.syncValue();
38898             this.iframe.className = 'x-hidden';
38899             this.el.removeClass('x-hidden');
38900             this.el.dom.removeAttribute('tabIndex');
38901             this.el.focus();
38902         }else{
38903              
38904             this.pushValue();
38905             this.iframe.className = '';
38906             this.el.addClass('x-hidden');
38907             this.el.dom.setAttribute('tabIndex', -1);
38908             this.deferFocus();
38909         }
38910         this.setSize(this.wrap.getSize());
38911         this.fireEvent('editmodechange', this, this.sourceEditMode);
38912     },
38913
38914     // private used internally
38915     createLink : function(){
38916         var url = prompt(this.createLinkText, this.defaultLinkValue);
38917         if(url && url != 'http:/'+'/'){
38918             this.relayCmd('createlink', url);
38919         }
38920     },
38921
38922     // private (for BoxComponent)
38923     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38924
38925     // private (for BoxComponent)
38926     getResizeEl : function(){
38927         return this.wrap;
38928     },
38929
38930     // private (for BoxComponent)
38931     getPositionEl : function(){
38932         return this.wrap;
38933     },
38934
38935     // private
38936     initEvents : function(){
38937         this.originalValue = this.getValue();
38938     },
38939
38940     /**
38941      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38942      * @method
38943      */
38944     markInvalid : Roo.emptyFn,
38945     /**
38946      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38947      * @method
38948      */
38949     clearInvalid : Roo.emptyFn,
38950
38951     setValue : function(v){
38952         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38953         this.pushValue();
38954     },
38955
38956     /**
38957      * Protected method that will not generally be called directly. If you need/want
38958      * custom HTML cleanup, this is the method you should override.
38959      * @param {String} html The HTML to be cleaned
38960      * return {String} The cleaned HTML
38961      */
38962     cleanHtml : function(html){
38963         html = String(html);
38964         if(html.length > 5){
38965             if(Roo.isSafari){ // strip safari nonsense
38966                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38967             }
38968         }
38969         if(html == '&nbsp;'){
38970             html = '';
38971         }
38972         return html;
38973     },
38974
38975     /**
38976      * Protected method that will not generally be called directly. Syncs the contents
38977      * of the editor iframe with the textarea.
38978      */
38979     syncValue : function(){
38980         if(this.initialized){
38981             var bd = (this.doc.body || this.doc.documentElement);
38982             this.cleanUpPaste();
38983             var html = bd.innerHTML;
38984             if(Roo.isSafari){
38985                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38986                 var m = bs.match(/text-align:(.*?);/i);
38987                 if(m && m[1]){
38988                     html = '<div style="'+m[0]+'">' + html + '</div>';
38989                 }
38990             }
38991             html = this.cleanHtml(html);
38992             if(this.fireEvent('beforesync', this, html) !== false){
38993                 this.el.dom.value = html;
38994                 this.fireEvent('sync', this, html);
38995             }
38996         }
38997     },
38998
38999     /**
39000      * Protected method that will not generally be called directly. Pushes the value of the textarea
39001      * into the iframe editor.
39002      */
39003     pushValue : function(){
39004         if(this.initialized){
39005             var v = this.el.dom.value;
39006             if(v.length < 1){
39007                 v = '&#160;';
39008             }
39009             
39010             if(this.fireEvent('beforepush', this, v) !== false){
39011                 var d = (this.doc.body || this.doc.documentElement);
39012                 d.innerHTML = v;
39013                 this.cleanUpPaste();
39014                 this.el.dom.value = d.innerHTML;
39015                 this.fireEvent('push', this, v);
39016             }
39017         }
39018     },
39019
39020     // private
39021     deferFocus : function(){
39022         this.focus.defer(10, this);
39023     },
39024
39025     // doc'ed in Field
39026     focus : function(){
39027         if(this.win && !this.sourceEditMode){
39028             this.win.focus();
39029         }else{
39030             this.el.focus();
39031         }
39032     },
39033     
39034     assignDocWin: function()
39035     {
39036         var iframe = this.iframe;
39037         
39038          if(Roo.isIE){
39039             this.doc = iframe.contentWindow.document;
39040             this.win = iframe.contentWindow;
39041         } else {
39042             if (!Roo.get(this.frameId)) {
39043                 return;
39044             }
39045             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39046             this.win = Roo.get(this.frameId).dom.contentWindow;
39047         }
39048     },
39049     
39050     // private
39051     initEditor : function(){
39052         //console.log("INIT EDITOR");
39053         this.assignDocWin();
39054         
39055         
39056         
39057         this.doc.designMode="on";
39058         this.doc.open();
39059         this.doc.write(this.getDocMarkup());
39060         this.doc.close();
39061         
39062         var dbody = (this.doc.body || this.doc.documentElement);
39063         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39064         // this copies styles from the containing element into thsi one..
39065         // not sure why we need all of this..
39066         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39067         ss['background-attachment'] = 'fixed'; // w3c
39068         dbody.bgProperties = 'fixed'; // ie
39069         Roo.DomHelper.applyStyles(dbody, ss);
39070         Roo.EventManager.on(this.doc, {
39071             //'mousedown': this.onEditorEvent,
39072             'mouseup': this.onEditorEvent,
39073             'dblclick': this.onEditorEvent,
39074             'click': this.onEditorEvent,
39075             'keyup': this.onEditorEvent,
39076             buffer:100,
39077             scope: this
39078         });
39079         if(Roo.isGecko){
39080             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39081         }
39082         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39083             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39084         }
39085         this.initialized = true;
39086
39087         this.fireEvent('initialize', this);
39088         this.pushValue();
39089     },
39090
39091     // private
39092     onDestroy : function(){
39093         
39094         
39095         
39096         if(this.rendered){
39097             
39098             for (var i =0; i < this.toolbars.length;i++) {
39099                 // fixme - ask toolbars for heights?
39100                 this.toolbars[i].onDestroy();
39101             }
39102             
39103             this.wrap.dom.innerHTML = '';
39104             this.wrap.remove();
39105         }
39106     },
39107
39108     // private
39109     onFirstFocus : function(){
39110         
39111         this.assignDocWin();
39112         
39113         
39114         this.activated = true;
39115         for (var i =0; i < this.toolbars.length;i++) {
39116             this.toolbars[i].onFirstFocus();
39117         }
39118        
39119         if(Roo.isGecko){ // prevent silly gecko errors
39120             this.win.focus();
39121             var s = this.win.getSelection();
39122             if(!s.focusNode || s.focusNode.nodeType != 3){
39123                 var r = s.getRangeAt(0);
39124                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39125                 r.collapse(true);
39126                 this.deferFocus();
39127             }
39128             try{
39129                 this.execCmd('useCSS', true);
39130                 this.execCmd('styleWithCSS', false);
39131             }catch(e){}
39132         }
39133         this.fireEvent('activate', this);
39134     },
39135
39136     // private
39137     adjustFont: function(btn){
39138         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39139         //if(Roo.isSafari){ // safari
39140         //    adjust *= 2;
39141        // }
39142         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39143         if(Roo.isSafari){ // safari
39144             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39145             v =  (v < 10) ? 10 : v;
39146             v =  (v > 48) ? 48 : v;
39147             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39148             
39149         }
39150         
39151         
39152         v = Math.max(1, v+adjust);
39153         
39154         this.execCmd('FontSize', v  );
39155     },
39156
39157     onEditorEvent : function(e){
39158         this.fireEvent('editorevent', this, e);
39159       //  this.updateToolbar();
39160         this.syncValue();
39161     },
39162
39163     insertTag : function(tg)
39164     {
39165         // could be a bit smarter... -> wrap the current selected tRoo..
39166         
39167         this.execCmd("formatblock",   tg);
39168         
39169     },
39170     
39171     insertText : function(txt)
39172     {
39173         
39174         
39175         range = this.createRange();
39176         range.deleteContents();
39177                //alert(Sender.getAttribute('label'));
39178                
39179         range.insertNode(this.doc.createTextNode(txt));
39180     } ,
39181     
39182     // private
39183     relayBtnCmd : function(btn){
39184         this.relayCmd(btn.cmd);
39185     },
39186
39187     /**
39188      * Executes a Midas editor command on the editor document and performs necessary focus and
39189      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39190      * @param {String} cmd The Midas command
39191      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39192      */
39193     relayCmd : function(cmd, value){
39194         this.win.focus();
39195         this.execCmd(cmd, value);
39196         this.fireEvent('editorevent', this);
39197         //this.updateToolbar();
39198         this.deferFocus();
39199     },
39200
39201     /**
39202      * Executes a Midas editor command directly on the editor document.
39203      * For visual commands, you should use {@link #relayCmd} instead.
39204      * <b>This should only be called after the editor is initialized.</b>
39205      * @param {String} cmd The Midas command
39206      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39207      */
39208     execCmd : function(cmd, value){
39209         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39210         this.syncValue();
39211     },
39212
39213    
39214     /**
39215      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39216      * to insert tRoo.
39217      * @param {String} text
39218      */
39219     insertAtCursor : function(text){
39220         if(!this.activated){
39221             return;
39222         }
39223         if(Roo.isIE){
39224             this.win.focus();
39225             var r = this.doc.selection.createRange();
39226             if(r){
39227                 r.collapse(true);
39228                 r.pasteHTML(text);
39229                 this.syncValue();
39230                 this.deferFocus();
39231             }
39232         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39233             this.win.focus();
39234             this.execCmd('InsertHTML', text);
39235             this.deferFocus();
39236         }
39237     },
39238  // private
39239     mozKeyPress : function(e){
39240         if(e.ctrlKey){
39241             var c = e.getCharCode(), cmd;
39242           
39243             if(c > 0){
39244                 c = String.fromCharCode(c).toLowerCase();
39245                 switch(c){
39246                     case 'b':
39247                         cmd = 'bold';
39248                     break;
39249                     case 'i':
39250                         cmd = 'italic';
39251                     break;
39252                     case 'u':
39253                         cmd = 'underline';
39254                     case 'v':
39255                         this.cleanUpPaste.defer(100, this);
39256                         return;
39257                     break;
39258                 }
39259                 if(cmd){
39260                     this.win.focus();
39261                     this.execCmd(cmd);
39262                     this.deferFocus();
39263                     e.preventDefault();
39264                 }
39265                 
39266             }
39267         }
39268     },
39269
39270     // private
39271     fixKeys : function(){ // load time branching for fastest keydown performance
39272         if(Roo.isIE){
39273             return function(e){
39274                 var k = e.getKey(), r;
39275                 if(k == e.TAB){
39276                     e.stopEvent();
39277                     r = this.doc.selection.createRange();
39278                     if(r){
39279                         r.collapse(true);
39280                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39281                         this.deferFocus();
39282                     }
39283                     return;
39284                 }
39285                 
39286                 if(k == e.ENTER){
39287                     r = this.doc.selection.createRange();
39288                     if(r){
39289                         var target = r.parentElement();
39290                         if(!target || target.tagName.toLowerCase() != 'li'){
39291                             e.stopEvent();
39292                             r.pasteHTML('<br />');
39293                             r.collapse(false);
39294                             r.select();
39295                         }
39296                     }
39297                 }
39298                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39299                     this.cleanUpPaste.defer(100, this);
39300                     return;
39301                 }
39302                 
39303                 
39304             };
39305         }else if(Roo.isOpera){
39306             return function(e){
39307                 var k = e.getKey();
39308                 if(k == e.TAB){
39309                     e.stopEvent();
39310                     this.win.focus();
39311                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39312                     this.deferFocus();
39313                 }
39314                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39315                     this.cleanUpPaste.defer(100, this);
39316                     return;
39317                 }
39318                 
39319             };
39320         }else if(Roo.isSafari){
39321             return function(e){
39322                 var k = e.getKey();
39323                 
39324                 if(k == e.TAB){
39325                     e.stopEvent();
39326                     this.execCmd('InsertText','\t');
39327                     this.deferFocus();
39328                     return;
39329                 }
39330                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39331                     this.cleanUpPaste.defer(100, this);
39332                     return;
39333                 }
39334                 
39335              };
39336         }
39337     }(),
39338     
39339     getAllAncestors: function()
39340     {
39341         var p = this.getSelectedNode();
39342         var a = [];
39343         if (!p) {
39344             a.push(p); // push blank onto stack..
39345             p = this.getParentElement();
39346         }
39347         
39348         
39349         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39350             a.push(p);
39351             p = p.parentNode;
39352         }
39353         a.push(this.doc.body);
39354         return a;
39355     },
39356     lastSel : false,
39357     lastSelNode : false,
39358     
39359     
39360     getSelection : function() 
39361     {
39362         this.assignDocWin();
39363         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39364     },
39365     
39366     getSelectedNode: function() 
39367     {
39368         // this may only work on Gecko!!!
39369         
39370         // should we cache this!!!!
39371         
39372         
39373         
39374          
39375         var range = this.createRange(this.getSelection()).cloneRange();
39376         
39377         if (Roo.isIE) {
39378             var parent = range.parentElement();
39379             while (true) {
39380                 var testRange = range.duplicate();
39381                 testRange.moveToElementText(parent);
39382                 if (testRange.inRange(range)) {
39383                     break;
39384                 }
39385                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39386                     break;
39387                 }
39388                 parent = parent.parentElement;
39389             }
39390             return parent;
39391         }
39392         
39393         // is ancestor a text element.
39394         var ac =  range.commonAncestorContainer;
39395         if (ac.nodeType == 3) {
39396             ac = ac.parentNode;
39397         }
39398         
39399         var ar = ac.childNodes;
39400          
39401         var nodes = [];
39402         var other_nodes = [];
39403         var has_other_nodes = false;
39404         for (var i=0;i<ar.length;i++) {
39405             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39406                 continue;
39407             }
39408             // fullly contained node.
39409             
39410             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39411                 nodes.push(ar[i]);
39412                 continue;
39413             }
39414             
39415             // probably selected..
39416             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39417                 other_nodes.push(ar[i]);
39418                 continue;
39419             }
39420             // outer..
39421             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39422                 continue;
39423             }
39424             
39425             
39426             has_other_nodes = true;
39427         }
39428         if (!nodes.length && other_nodes.length) {
39429             nodes= other_nodes;
39430         }
39431         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39432             return false;
39433         }
39434         
39435         return nodes[0];
39436     },
39437     createRange: function(sel)
39438     {
39439         // this has strange effects when using with 
39440         // top toolbar - not sure if it's a great idea.
39441         //this.editor.contentWindow.focus();
39442         if (typeof sel != "undefined") {
39443             try {
39444                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39445             } catch(e) {
39446                 return this.doc.createRange();
39447             }
39448         } else {
39449             return this.doc.createRange();
39450         }
39451     },
39452     getParentElement: function()
39453     {
39454         
39455         this.assignDocWin();
39456         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39457         
39458         var range = this.createRange(sel);
39459          
39460         try {
39461             var p = range.commonAncestorContainer;
39462             while (p.nodeType == 3) { // text node
39463                 p = p.parentNode;
39464             }
39465             return p;
39466         } catch (e) {
39467             return null;
39468         }
39469     
39470     },
39471     /***
39472      *
39473      * Range intersection.. the hard stuff...
39474      *  '-1' = before
39475      *  '0' = hits..
39476      *  '1' = after.
39477      *         [ -- selected range --- ]
39478      *   [fail]                        [fail]
39479      *
39480      *    basically..
39481      *      if end is before start or  hits it. fail.
39482      *      if start is after end or hits it fail.
39483      *
39484      *   if either hits (but other is outside. - then it's not 
39485      *   
39486      *    
39487      **/
39488     
39489     
39490     // @see http://www.thismuchiknow.co.uk/?p=64.
39491     rangeIntersectsNode : function(range, node)
39492     {
39493         var nodeRange = node.ownerDocument.createRange();
39494         try {
39495             nodeRange.selectNode(node);
39496         } catch (e) {
39497             nodeRange.selectNodeContents(node);
39498         }
39499     
39500         var rangeStartRange = range.cloneRange();
39501         rangeStartRange.collapse(true);
39502     
39503         var rangeEndRange = range.cloneRange();
39504         rangeEndRange.collapse(false);
39505     
39506         var nodeStartRange = nodeRange.cloneRange();
39507         nodeStartRange.collapse(true);
39508     
39509         var nodeEndRange = nodeRange.cloneRange();
39510         nodeEndRange.collapse(false);
39511     
39512         return rangeStartRange.compareBoundaryPoints(
39513                  Range.START_TO_START, nodeEndRange) == -1 &&
39514                rangeEndRange.compareBoundaryPoints(
39515                  Range.START_TO_START, nodeStartRange) == 1;
39516         
39517          
39518     },
39519     rangeCompareNode : function(range, node)
39520     {
39521         var nodeRange = node.ownerDocument.createRange();
39522         try {
39523             nodeRange.selectNode(node);
39524         } catch (e) {
39525             nodeRange.selectNodeContents(node);
39526         }
39527         
39528         
39529         range.collapse(true);
39530     
39531         nodeRange.collapse(true);
39532      
39533         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39534         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39535          
39536         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39537         
39538         var nodeIsBefore   =  ss == 1;
39539         var nodeIsAfter    = ee == -1;
39540         
39541         if (nodeIsBefore && nodeIsAfter)
39542             return 0; // outer
39543         if (!nodeIsBefore && nodeIsAfter)
39544             return 1; //right trailed.
39545         
39546         if (nodeIsBefore && !nodeIsAfter)
39547             return 2;  // left trailed.
39548         // fully contined.
39549         return 3;
39550     },
39551
39552     // private? - in a new class?
39553     cleanUpPaste :  function()
39554     {
39555         // cleans up the whole document..
39556       //  console.log('cleanuppaste');
39557         this.cleanUpChildren(this.doc.body);
39558         
39559         
39560     },
39561     cleanUpChildren : function (n)
39562     {
39563         if (!n.childNodes.length) {
39564             return;
39565         }
39566         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39567            this.cleanUpChild(n.childNodes[i]);
39568         }
39569     },
39570     
39571     
39572         
39573     
39574     cleanUpChild : function (node)
39575     {
39576         //console.log(node);
39577         if (node.nodeName == "#text") {
39578             // clean up silly Windows -- stuff?
39579             return; 
39580         }
39581         if (node.nodeName == "#comment") {
39582             node.parentNode.removeChild(node);
39583             // clean up silly Windows -- stuff?
39584             return; 
39585         }
39586         
39587         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39588             // remove node.
39589             node.parentNode.removeChild(node);
39590             return;
39591             
39592         }
39593         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39594             this.cleanUpChildren(node);
39595             // inserts everything just before this node...
39596             while (node.childNodes.length) {
39597                 var cn = node.childNodes[0];
39598                 node.removeChild(cn);
39599                 node.parentNode.insertBefore(cn, node);
39600             }
39601             node.parentNode.removeChild(node);
39602             return;
39603         }
39604         
39605         if (!node.attributes || !node.attributes.length) {
39606             this.cleanUpChildren(node);
39607             return;
39608         }
39609         
39610         function cleanAttr(n,v)
39611         {
39612             
39613             if (v.match(/^\./) || v.match(/^\//)) {
39614                 return;
39615             }
39616             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39617                 return;
39618             }
39619             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39620             node.removeAttribute(n);
39621             
39622         }
39623         
39624         function cleanStyle(n,v)
39625         {
39626             if (v.match(/expression/)) { //XSS?? should we even bother..
39627                 node.removeAttribute(n);
39628                 return;
39629             }
39630             
39631             
39632             var parts = v.split(/;/);
39633             Roo.each(parts, function(p) {
39634                 p = p.replace(/\s+/g,'');
39635                 if (!p.length) {
39636                     return true;
39637                 }
39638                 var l = p.split(':').shift().replace(/\s+/g,'');
39639                 
39640                 // only allow 'c whitelisted system attributes'
39641                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39642                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39643                     node.removeAttribute(n);
39644                     return false;
39645                 }
39646                 return true;
39647             });
39648             
39649             
39650         }
39651         
39652         
39653         for (var i = node.attributes.length-1; i > -1 ; i--) {
39654             var a = node.attributes[i];
39655             //console.log(a);
39656             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39657                 node.removeAttribute(a.name);
39658                 return;
39659             }
39660             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39661                 cleanAttr(a.name,a.value); // fixme..
39662                 return;
39663             }
39664             if (a.name == 'style') {
39665                 cleanStyle(a.name,a.value);
39666             }
39667             /// clean up MS crap..
39668             if (a.name == 'class') {
39669                 if (a.value.match(/^Mso/)) {
39670                     node.className = '';
39671                 }
39672             }
39673             
39674             // style cleanup!?
39675             // class cleanup?
39676             
39677         }
39678         
39679         
39680         this.cleanUpChildren(node);
39681         
39682         
39683     }
39684     
39685     
39686     // hide stuff that is not compatible
39687     /**
39688      * @event blur
39689      * @hide
39690      */
39691     /**
39692      * @event change
39693      * @hide
39694      */
39695     /**
39696      * @event focus
39697      * @hide
39698      */
39699     /**
39700      * @event specialkey
39701      * @hide
39702      */
39703     /**
39704      * @cfg {String} fieldClass @hide
39705      */
39706     /**
39707      * @cfg {String} focusClass @hide
39708      */
39709     /**
39710      * @cfg {String} autoCreate @hide
39711      */
39712     /**
39713      * @cfg {String} inputType @hide
39714      */
39715     /**
39716      * @cfg {String} invalidClass @hide
39717      */
39718     /**
39719      * @cfg {String} invalidText @hide
39720      */
39721     /**
39722      * @cfg {String} msgFx @hide
39723      */
39724     /**
39725      * @cfg {String} validateOnBlur @hide
39726      */
39727 });
39728
39729 Roo.form.HtmlEditor.white = [
39730         'area', 'br', 'img', 'input', 'hr', 'wbr',
39731         
39732        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39733        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39734        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39735        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39736        'table',   'ul',         'xmp', 
39737        
39738        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39739       'thead',   'tr', 
39740      
39741       'dir', 'menu', 'ol', 'ul', 'dl',
39742        
39743       'embed',  'object'
39744 ];
39745
39746
39747 Roo.form.HtmlEditor.black = [
39748     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39749         'applet', // 
39750         'base',   'basefont', 'bgsound', 'blink',  'body', 
39751         'frame',  'frameset', 'head',    'html',   'ilayer', 
39752         'iframe', 'layer',  'link',     'meta',    'object',   
39753         'script', 'style' ,'title',  'xml' // clean later..
39754 ];
39755 Roo.form.HtmlEditor.clean = [
39756     'script', 'style', 'title', 'xml'
39757 ];
39758 Roo.form.HtmlEditor.remove = [
39759     'font'
39760 ];
39761 // attributes..
39762
39763 Roo.form.HtmlEditor.ablack = [
39764     'on'
39765 ];
39766     
39767 Roo.form.HtmlEditor.aclean = [ 
39768     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39769 ];
39770
39771 // protocols..
39772 Roo.form.HtmlEditor.pwhite= [
39773         'http',  'https',  'mailto'
39774 ];
39775
39776 // white listed style attributes.
39777 Roo.form.HtmlEditor.cwhite= [
39778         'text-align',
39779         'font-size'
39780 ];
39781
39782 // <script type="text/javascript">
39783 /*
39784  * Based on
39785  * Ext JS Library 1.1.1
39786  * Copyright(c) 2006-2007, Ext JS, LLC.
39787  *  
39788  
39789  */
39790
39791 /**
39792  * @class Roo.form.HtmlEditorToolbar1
39793  * Basic Toolbar
39794  * 
39795  * Usage:
39796  *
39797  new Roo.form.HtmlEditor({
39798     ....
39799     toolbars : [
39800         new Roo.form.HtmlEditorToolbar1({
39801             disable : { fonts: 1 , format: 1, ..., ... , ...],
39802             btns : [ .... ]
39803         })
39804     }
39805      
39806  * 
39807  * @cfg {Object} disable List of elements to disable..
39808  * @cfg {Array} btns List of additional buttons.
39809  * 
39810  * 
39811  * NEEDS Extra CSS? 
39812  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39813  */
39814  
39815 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39816 {
39817     
39818     Roo.apply(this, config);
39819     
39820     // default disabled, based on 'good practice'..
39821     this.disable = this.disable || {};
39822     Roo.applyIf(this.disable, {
39823         fontSize : true,
39824         colors : true,
39825         specialElements : true
39826     });
39827     
39828     
39829     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39830     // dont call parent... till later.
39831 }
39832
39833 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39834     
39835     tb: false,
39836     
39837     rendered: false,
39838     
39839     editor : false,
39840     /**
39841      * @cfg {Object} disable  List of toolbar elements to disable
39842          
39843      */
39844     disable : false,
39845       /**
39846      * @cfg {Array} fontFamilies An array of available font families
39847      */
39848     fontFamilies : [
39849         'Arial',
39850         'Courier New',
39851         'Tahoma',
39852         'Times New Roman',
39853         'Verdana'
39854     ],
39855     
39856     specialChars : [
39857            "&#169;",
39858           "&#174;",     
39859           "&#8482;",    
39860           "&#163;" ,    
39861          // "&#8212;",    
39862           "&#8230;",    
39863           "&#247;" ,    
39864         //  "&#225;" ,     ?? a acute?
39865            "&#8364;"    , //Euro
39866        //   "&#8220;"    ,
39867         //  "&#8221;"    ,
39868         //  "&#8226;"    ,
39869           "&#176;"  //   , // degrees
39870
39871          // "&#233;"     , // e ecute
39872          // "&#250;"     , // u ecute?
39873     ],
39874     
39875     specialElements : [
39876         {
39877             text: "Insert Table",
39878             xtype: 'MenuItem',
39879             xns : Roo.Menu,
39880             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39881                 
39882         },
39883         {    
39884             text: "Insert Image",
39885             xtype: 'MenuItem',
39886             xns : Roo.Menu,
39887             ihtml : '<img src="about:blank"/>'
39888             
39889         }
39890         
39891          
39892     ],
39893     
39894     
39895     inputElements : [ 
39896             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39897             "input:submit", "input:button", "select", "textarea", "label" ],
39898     formats : [
39899         ["p"] ,  
39900         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39901         ["pre"],[ "code"], 
39902         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39903     ],
39904      /**
39905      * @cfg {String} defaultFont default font to use.
39906      */
39907     defaultFont: 'tahoma',
39908    
39909     fontSelect : false,
39910     
39911     
39912     formatCombo : false,
39913     
39914     init : function(editor)
39915     {
39916         this.editor = editor;
39917         
39918         
39919         var fid = editor.frameId;
39920         var etb = this;
39921         function btn(id, toggle, handler){
39922             var xid = fid + '-'+ id ;
39923             return {
39924                 id : xid,
39925                 cmd : id,
39926                 cls : 'x-btn-icon x-edit-'+id,
39927                 enableToggle:toggle !== false,
39928                 scope: editor, // was editor...
39929                 handler:handler||editor.relayBtnCmd,
39930                 clickEvent:'mousedown',
39931                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39932                 tabIndex:-1
39933             };
39934         }
39935         
39936         
39937         
39938         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39939         this.tb = tb;
39940          // stop form submits
39941         tb.el.on('click', function(e){
39942             e.preventDefault(); // what does this do?
39943         });
39944
39945         if(!this.disable.font && !Roo.isSafari){
39946             /* why no safari for fonts
39947             editor.fontSelect = tb.el.createChild({
39948                 tag:'select',
39949                 tabIndex: -1,
39950                 cls:'x-font-select',
39951                 html: editor.createFontOptions()
39952             });
39953             editor.fontSelect.on('change', function(){
39954                 var font = editor.fontSelect.dom.value;
39955                 editor.relayCmd('fontname', font);
39956                 editor.deferFocus();
39957             }, editor);
39958             tb.add(
39959                 editor.fontSelect.dom,
39960                 '-'
39961             );
39962             */
39963         };
39964         if(!this.disable.formats){
39965             this.formatCombo = new Roo.form.ComboBox({
39966                 store: new Roo.data.SimpleStore({
39967                     id : 'tag',
39968                     fields: ['tag'],
39969                     data : this.formats // from states.js
39970                 }),
39971                 blockFocus : true,
39972                 //autoCreate : {tag: "div",  size: "20"},
39973                 displayField:'tag',
39974                 typeAhead: false,
39975                 mode: 'local',
39976                 editable : false,
39977                 triggerAction: 'all',
39978                 emptyText:'Add tag',
39979                 selectOnFocus:true,
39980                 width:135,
39981                 listeners : {
39982                     'select': function(c, r, i) {
39983                         editor.insertTag(r.get('tag'));
39984                         editor.focus();
39985                     }
39986                 }
39987
39988             });
39989             tb.addField(this.formatCombo);
39990             
39991         }
39992         
39993         if(!this.disable.format){
39994             tb.add(
39995                 btn('bold'),
39996                 btn('italic'),
39997                 btn('underline')
39998             );
39999         };
40000         if(!this.disable.fontSize){
40001             tb.add(
40002                 '-',
40003                 
40004                 
40005                 btn('increasefontsize', false, editor.adjustFont),
40006                 btn('decreasefontsize', false, editor.adjustFont)
40007             );
40008         };
40009         
40010         
40011         if(!this.disable.colors){
40012             tb.add(
40013                 '-', {
40014                     id:editor.frameId +'-forecolor',
40015                     cls:'x-btn-icon x-edit-forecolor',
40016                     clickEvent:'mousedown',
40017                     tooltip: this.buttonTips['forecolor'] || undefined,
40018                     tabIndex:-1,
40019                     menu : new Roo.menu.ColorMenu({
40020                         allowReselect: true,
40021                         focus: Roo.emptyFn,
40022                         value:'000000',
40023                         plain:true,
40024                         selectHandler: function(cp, color){
40025                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40026                             editor.deferFocus();
40027                         },
40028                         scope: editor,
40029                         clickEvent:'mousedown'
40030                     })
40031                 }, {
40032                     id:editor.frameId +'backcolor',
40033                     cls:'x-btn-icon x-edit-backcolor',
40034                     clickEvent:'mousedown',
40035                     tooltip: this.buttonTips['backcolor'] || undefined,
40036                     tabIndex:-1,
40037                     menu : new Roo.menu.ColorMenu({
40038                         focus: Roo.emptyFn,
40039                         value:'FFFFFF',
40040                         plain:true,
40041                         allowReselect: true,
40042                         selectHandler: function(cp, color){
40043                             if(Roo.isGecko){
40044                                 editor.execCmd('useCSS', false);
40045                                 editor.execCmd('hilitecolor', color);
40046                                 editor.execCmd('useCSS', true);
40047                                 editor.deferFocus();
40048                             }else{
40049                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40050                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40051                                 editor.deferFocus();
40052                             }
40053                         },
40054                         scope:editor,
40055                         clickEvent:'mousedown'
40056                     })
40057                 }
40058             );
40059         };
40060         // now add all the items...
40061         
40062
40063         if(!this.disable.alignments){
40064             tb.add(
40065                 '-',
40066                 btn('justifyleft'),
40067                 btn('justifycenter'),
40068                 btn('justifyright')
40069             );
40070         };
40071
40072         //if(!Roo.isSafari){
40073             if(!this.disable.links){
40074                 tb.add(
40075                     '-',
40076                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40077                 );
40078             };
40079
40080             if(!this.disable.lists){
40081                 tb.add(
40082                     '-',
40083                     btn('insertorderedlist'),
40084                     btn('insertunorderedlist')
40085                 );
40086             }
40087             if(!this.disable.sourceEdit){
40088                 tb.add(
40089                     '-',
40090                     btn('sourceedit', true, function(btn){
40091                         this.toggleSourceEdit(btn.pressed);
40092                     })
40093                 );
40094             }
40095         //}
40096         
40097         var smenu = { };
40098         // special menu.. - needs to be tidied up..
40099         if (!this.disable.special) {
40100             smenu = {
40101                 text: "&#169;",
40102                 cls: 'x-edit-none',
40103                 
40104                 menu : {
40105                     items : []
40106                 }
40107             };
40108             for (var i =0; i < this.specialChars.length; i++) {
40109                 smenu.menu.items.push({
40110                     
40111                     html: this.specialChars[i],
40112                     handler: function(a,b) {
40113                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40114                         
40115                     },
40116                     tabIndex:-1
40117                 });
40118             }
40119             
40120             
40121             tb.add(smenu);
40122             
40123             
40124         }
40125          
40126         if (!this.disable.specialElements) {
40127             var semenu = {
40128                 text: "Other;",
40129                 cls: 'x-edit-none',
40130                 menu : {
40131                     items : []
40132                 }
40133             };
40134             for (var i =0; i < this.specialElements.length; i++) {
40135                 semenu.menu.items.push(
40136                     Roo.apply({ 
40137                         handler: function(a,b) {
40138                             editor.insertAtCursor(this.ihtml);
40139                         }
40140                     }, this.specialElements[i])
40141                 );
40142                     
40143             }
40144             
40145             tb.add(semenu);
40146             
40147             
40148         }
40149          
40150         
40151         if (this.btns) {
40152             for(var i =0; i< this.btns.length;i++) {
40153                 var b = this.btns[i];
40154                 b.cls =  'x-edit-none';
40155                 b.scope = editor;
40156                 tb.add(b);
40157             }
40158         
40159         }
40160         
40161         
40162         
40163         // disable everything...
40164         
40165         this.tb.items.each(function(item){
40166            if(item.id != editor.frameId+ '-sourceedit'){
40167                 item.disable();
40168             }
40169         });
40170         this.rendered = true;
40171         
40172         // the all the btns;
40173         editor.on('editorevent', this.updateToolbar, this);
40174         // other toolbars need to implement this..
40175         //editor.on('editmodechange', this.updateToolbar, this);
40176     },
40177     
40178     
40179     
40180     /**
40181      * Protected method that will not generally be called directly. It triggers
40182      * a toolbar update by reading the markup state of the current selection in the editor.
40183      */
40184     updateToolbar: function(){
40185
40186         if(!this.editor.activated){
40187             this.editor.onFirstFocus();
40188             return;
40189         }
40190
40191         var btns = this.tb.items.map, 
40192             doc = this.editor.doc,
40193             frameId = this.editor.frameId;
40194
40195         if(!this.disable.font && !Roo.isSafari){
40196             /*
40197             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40198             if(name != this.fontSelect.dom.value){
40199                 this.fontSelect.dom.value = name;
40200             }
40201             */
40202         }
40203         if(!this.disable.format){
40204             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40205             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40206             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40207         }
40208         if(!this.disable.alignments){
40209             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40210             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40211             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40212         }
40213         if(!Roo.isSafari && !this.disable.lists){
40214             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40215             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40216         }
40217         
40218         var ans = this.editor.getAllAncestors();
40219         if (this.formatCombo) {
40220             
40221             
40222             var store = this.formatCombo.store;
40223             this.formatCombo.setValue("");
40224             for (var i =0; i < ans.length;i++) {
40225                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40226                     // select it..
40227                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40228                     break;
40229                 }
40230             }
40231         }
40232         
40233         
40234         
40235         // hides menus... - so this cant be on a menu...
40236         Roo.menu.MenuMgr.hideAll();
40237
40238         //this.editorsyncValue();
40239     },
40240    
40241     
40242     createFontOptions : function(){
40243         var buf = [], fs = this.fontFamilies, ff, lc;
40244         for(var i = 0, len = fs.length; i< len; i++){
40245             ff = fs[i];
40246             lc = ff.toLowerCase();
40247             buf.push(
40248                 '<option value="',lc,'" style="font-family:',ff,';"',
40249                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40250                     ff,
40251                 '</option>'
40252             );
40253         }
40254         return buf.join('');
40255     },
40256     
40257     toggleSourceEdit : function(sourceEditMode){
40258         if(sourceEditMode === undefined){
40259             sourceEditMode = !this.sourceEditMode;
40260         }
40261         this.sourceEditMode = sourceEditMode === true;
40262         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40263         // just toggle the button?
40264         if(btn.pressed !== this.editor.sourceEditMode){
40265             btn.toggle(this.editor.sourceEditMode);
40266             return;
40267         }
40268         
40269         if(this.sourceEditMode){
40270             this.tb.items.each(function(item){
40271                 if(item.cmd != 'sourceedit'){
40272                     item.disable();
40273                 }
40274             });
40275           
40276         }else{
40277             if(this.initialized){
40278                 this.tb.items.each(function(item){
40279                     item.enable();
40280                 });
40281             }
40282             
40283         }
40284         // tell the editor that it's been pressed..
40285         this.editor.toggleSourceEdit(sourceEditMode);
40286        
40287     },
40288      /**
40289      * Object collection of toolbar tooltips for the buttons in the editor. The key
40290      * is the command id associated with that button and the value is a valid QuickTips object.
40291      * For example:
40292 <pre><code>
40293 {
40294     bold : {
40295         title: 'Bold (Ctrl+B)',
40296         text: 'Make the selected text bold.',
40297         cls: 'x-html-editor-tip'
40298     },
40299     italic : {
40300         title: 'Italic (Ctrl+I)',
40301         text: 'Make the selected text italic.',
40302         cls: 'x-html-editor-tip'
40303     },
40304     ...
40305 </code></pre>
40306     * @type Object
40307      */
40308     buttonTips : {
40309         bold : {
40310             title: 'Bold (Ctrl+B)',
40311             text: 'Make the selected text bold.',
40312             cls: 'x-html-editor-tip'
40313         },
40314         italic : {
40315             title: 'Italic (Ctrl+I)',
40316             text: 'Make the selected text italic.',
40317             cls: 'x-html-editor-tip'
40318         },
40319         underline : {
40320             title: 'Underline (Ctrl+U)',
40321             text: 'Underline the selected text.',
40322             cls: 'x-html-editor-tip'
40323         },
40324         increasefontsize : {
40325             title: 'Grow Text',
40326             text: 'Increase the font size.',
40327             cls: 'x-html-editor-tip'
40328         },
40329         decreasefontsize : {
40330             title: 'Shrink Text',
40331             text: 'Decrease the font size.',
40332             cls: 'x-html-editor-tip'
40333         },
40334         backcolor : {
40335             title: 'Text Highlight Color',
40336             text: 'Change the background color of the selected text.',
40337             cls: 'x-html-editor-tip'
40338         },
40339         forecolor : {
40340             title: 'Font Color',
40341             text: 'Change the color of the selected text.',
40342             cls: 'x-html-editor-tip'
40343         },
40344         justifyleft : {
40345             title: 'Align Text Left',
40346             text: 'Align text to the left.',
40347             cls: 'x-html-editor-tip'
40348         },
40349         justifycenter : {
40350             title: 'Center Text',
40351             text: 'Center text in the editor.',
40352             cls: 'x-html-editor-tip'
40353         },
40354         justifyright : {
40355             title: 'Align Text Right',
40356             text: 'Align text to the right.',
40357             cls: 'x-html-editor-tip'
40358         },
40359         insertunorderedlist : {
40360             title: 'Bullet List',
40361             text: 'Start a bulleted list.',
40362             cls: 'x-html-editor-tip'
40363         },
40364         insertorderedlist : {
40365             title: 'Numbered List',
40366             text: 'Start a numbered list.',
40367             cls: 'x-html-editor-tip'
40368         },
40369         createlink : {
40370             title: 'Hyperlink',
40371             text: 'Make the selected text a hyperlink.',
40372             cls: 'x-html-editor-tip'
40373         },
40374         sourceedit : {
40375             title: 'Source Edit',
40376             text: 'Switch to source editing mode.',
40377             cls: 'x-html-editor-tip'
40378         }
40379     },
40380     // private
40381     onDestroy : function(){
40382         if(this.rendered){
40383             
40384             this.tb.items.each(function(item){
40385                 if(item.menu){
40386                     item.menu.removeAll();
40387                     if(item.menu.el){
40388                         item.menu.el.destroy();
40389                     }
40390                 }
40391                 item.destroy();
40392             });
40393              
40394         }
40395     },
40396     onFirstFocus: function() {
40397         this.tb.items.each(function(item){
40398            item.enable();
40399         });
40400     }
40401 });
40402
40403
40404
40405
40406 // <script type="text/javascript">
40407 /*
40408  * Based on
40409  * Ext JS Library 1.1.1
40410  * Copyright(c) 2006-2007, Ext JS, LLC.
40411  *  
40412  
40413  */
40414
40415  
40416 /**
40417  * @class Roo.form.HtmlEditor.ToolbarContext
40418  * Context Toolbar
40419  * 
40420  * Usage:
40421  *
40422  new Roo.form.HtmlEditor({
40423     ....
40424     toolbars : [
40425         { xtype: 'ToolbarStandard', styles : {} }
40426         { xtype: 'ToolbarContext', disable : {} }
40427     ]
40428 })
40429
40430      
40431  * 
40432  * @config : {Object} disable List of elements to disable.. (not done yet.)
40433  * @config : {Object} styles  Map of styles available.
40434  * 
40435  */
40436
40437 Roo.form.HtmlEditor.ToolbarContext = function(config)
40438 {
40439     
40440     Roo.apply(this, config);
40441     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40442     // dont call parent... till later.
40443     this.styles = this.styles || {};
40444 }
40445 Roo.form.HtmlEditor.ToolbarContext.types = {
40446     'IMG' : {
40447         width : {
40448             title: "Width",
40449             width: 40
40450         },
40451         height:  {
40452             title: "Height",
40453             width: 40
40454         },
40455         align: {
40456             title: "Align",
40457             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40458             width : 80
40459             
40460         },
40461         border: {
40462             title: "Border",
40463             width: 40
40464         },
40465         alt: {
40466             title: "Alt",
40467             width: 120
40468         },
40469         src : {
40470             title: "Src",
40471             width: 220
40472         }
40473         
40474     },
40475     'A' : {
40476         name : {
40477             title: "Name",
40478             width: 50
40479         },
40480         href:  {
40481             title: "Href",
40482             width: 220
40483         } // border?
40484         
40485     },
40486     'TABLE' : {
40487         rows : {
40488             title: "Rows",
40489             width: 20
40490         },
40491         cols : {
40492             title: "Cols",
40493             width: 20
40494         },
40495         width : {
40496             title: "Width",
40497             width: 40
40498         },
40499         height : {
40500             title: "Height",
40501             width: 40
40502         },
40503         border : {
40504             title: "Border",
40505             width: 20
40506         }
40507     },
40508     'TD' : {
40509         width : {
40510             title: "Width",
40511             width: 40
40512         },
40513         height : {
40514             title: "Height",
40515             width: 40
40516         },   
40517         align: {
40518             title: "Align",
40519             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40520             width: 80
40521         },
40522         valign: {
40523             title: "Valign",
40524             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40525             width: 80
40526         },
40527         colspan: {
40528             title: "Colspan",
40529             width: 20
40530             
40531         }
40532     },
40533     'INPUT' : {
40534         name : {
40535             title: "name",
40536             width: 120
40537         },
40538         value : {
40539             title: "Value",
40540             width: 120
40541         },
40542         width : {
40543             title: "Width",
40544             width: 40
40545         }
40546     },
40547     'LABEL' : {
40548         'for' : {
40549             title: "For",
40550             width: 120
40551         }
40552     },
40553     'TEXTAREA' : {
40554           name : {
40555             title: "name",
40556             width: 120
40557         },
40558         rows : {
40559             title: "Rows",
40560             width: 20
40561         },
40562         cols : {
40563             title: "Cols",
40564             width: 20
40565         }
40566     },
40567     'SELECT' : {
40568         name : {
40569             title: "name",
40570             width: 120
40571         },
40572         selectoptions : {
40573             title: "Options",
40574             width: 200
40575         }
40576     },
40577     
40578     // should we really allow this??
40579     // should this just be 
40580     'BODY' : {
40581         title : {
40582             title: "title",
40583             width: 200,
40584             disabled : true
40585         }
40586     },
40587     '*' : {
40588         // empty..
40589     }
40590 };
40591
40592
40593
40594 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40595     
40596     tb: false,
40597     
40598     rendered: false,
40599     
40600     editor : false,
40601     /**
40602      * @cfg {Object} disable  List of toolbar elements to disable
40603          
40604      */
40605     disable : false,
40606     /**
40607      * @cfg {Object} styles List of styles 
40608      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40609      *
40610      * These must be defined in the page, so they get rendered correctly..
40611      * .headline { }
40612      * TD.underline { }
40613      * 
40614      */
40615     styles : false,
40616     
40617     
40618     
40619     toolbars : false,
40620     
40621     init : function(editor)
40622     {
40623         this.editor = editor;
40624         
40625         
40626         var fid = editor.frameId;
40627         var etb = this;
40628         function btn(id, toggle, handler){
40629             var xid = fid + '-'+ id ;
40630             return {
40631                 id : xid,
40632                 cmd : id,
40633                 cls : 'x-btn-icon x-edit-'+id,
40634                 enableToggle:toggle !== false,
40635                 scope: editor, // was editor...
40636                 handler:handler||editor.relayBtnCmd,
40637                 clickEvent:'mousedown',
40638                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40639                 tabIndex:-1
40640             };
40641         }
40642         // create a new element.
40643         var wdiv = editor.wrap.createChild({
40644                 tag: 'div'
40645             }, editor.wrap.dom.firstChild.nextSibling, true);
40646         
40647         // can we do this more than once??
40648         
40649          // stop form submits
40650       
40651  
40652         // disable everything...
40653         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40654         this.toolbars = {};
40655            
40656         for (var i in  ty) {
40657           
40658             this.toolbars[i] = this.buildToolbar(ty[i],i);
40659         }
40660         this.tb = this.toolbars.BODY;
40661         this.tb.el.show();
40662         this.buildFooter();
40663         this.footer.show();
40664          
40665         this.rendered = true;
40666         
40667         // the all the btns;
40668         editor.on('editorevent', this.updateToolbar, this);
40669         // other toolbars need to implement this..
40670         //editor.on('editmodechange', this.updateToolbar, this);
40671     },
40672     
40673     
40674     
40675     /**
40676      * Protected method that will not generally be called directly. It triggers
40677      * a toolbar update by reading the markup state of the current selection in the editor.
40678      */
40679     updateToolbar: function(ignore_a,ignore_b,sel){
40680
40681         
40682         if(!this.editor.activated){
40683              this.editor.onFirstFocus();
40684             return;
40685         }
40686         var updateFooter = sel ? false : true;
40687         
40688         
40689         var ans = this.editor.getAllAncestors();
40690         
40691         // pick
40692         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40693         
40694         if (!sel) { 
40695             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40696             sel = sel ? sel : this.editor.doc.body;
40697             sel = sel.tagName.length ? sel : this.editor.doc.body;
40698             
40699         }
40700         // pick a menu that exists..
40701         var tn = sel.tagName.toUpperCase();
40702         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40703         
40704         tn = sel.tagName.toUpperCase();
40705         
40706         var lastSel = this.tb.selectedNode
40707         
40708         this.tb.selectedNode = sel;
40709         
40710         // if current menu does not match..
40711         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40712                 
40713             this.tb.el.hide();
40714             ///console.log("show: " + tn);
40715             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40716             this.tb.el.show();
40717             // update name
40718             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40719             
40720             
40721             // update attributes
40722             if (this.tb.fields) {
40723                 this.tb.fields.each(function(e) {
40724                    e.setValue(sel.getAttribute(e.name));
40725                 });
40726             }
40727             
40728             // update styles
40729             var st = this.tb.fields.item(0);
40730             st.store.removeAll();
40731             var cn = sel.className.split(/\s+/);
40732             
40733             var avs = [];
40734             if (this.styles['*']) {
40735                 
40736                 Roo.each(this.styles['*'], function(v) {
40737                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40738                 });
40739             }
40740             if (this.styles[tn]) { 
40741                 Roo.each(this.styles[tn], function(v) {
40742                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40743                 });
40744             }
40745             
40746             st.store.loadData(avs);
40747             st.collapse();
40748             st.setValue(cn);
40749             
40750             // flag our selected Node.
40751             this.tb.selectedNode = sel;
40752            
40753            
40754             Roo.menu.MenuMgr.hideAll();
40755
40756         }
40757         
40758         if (!updateFooter) {
40759             return;
40760         }
40761         // update the footer
40762         //
40763         var html = '';
40764         
40765         this.footerEls = ans.reverse();
40766         Roo.each(this.footerEls, function(a,i) {
40767             if (!a) { return; }
40768             html += html.length ? ' &gt; '  :  '';
40769             
40770             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40771             
40772         });
40773        
40774         // 
40775         var sz = this.footDisp.up('td').getSize();
40776         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40777         this.footDisp.dom.style.marginLeft = '5px';
40778         
40779         this.footDisp.dom.style.overflow = 'hidden';
40780         
40781         this.footDisp.dom.innerHTML = html;
40782             
40783         //this.editorsyncValue();
40784     },
40785    
40786        
40787     // private
40788     onDestroy : function(){
40789         if(this.rendered){
40790             
40791             this.tb.items.each(function(item){
40792                 if(item.menu){
40793                     item.menu.removeAll();
40794                     if(item.menu.el){
40795                         item.menu.el.destroy();
40796                     }
40797                 }
40798                 item.destroy();
40799             });
40800              
40801         }
40802     },
40803     onFirstFocus: function() {
40804         // need to do this for all the toolbars..
40805         this.tb.items.each(function(item){
40806            item.enable();
40807         });
40808     },
40809     buildToolbar: function(tlist, nm)
40810     {
40811         var editor = this.editor;
40812          // create a new element.
40813         var wdiv = editor.wrap.createChild({
40814                 tag: 'div'
40815             }, editor.wrap.dom.firstChild.nextSibling, true);
40816         
40817        
40818         var tb = new Roo.Toolbar(wdiv);
40819         // add the name..
40820         
40821         tb.add(nm+ ":&nbsp;");
40822         
40823         // styles...
40824         if (this.styles) {
40825             
40826             // this needs a multi-select checkbox...
40827             tb.addField( new Roo.form.ComboBox({
40828                 store: new Roo.data.SimpleStore({
40829                     id : 'val',
40830                     fields: ['val', 'selected'],
40831                     data : [] 
40832                 }),
40833                 name : 'className',
40834                 displayField:'val',
40835                 typeAhead: false,
40836                 mode: 'local',
40837                 editable : false,
40838                 triggerAction: 'all',
40839                 emptyText:'Select Style',
40840                 selectOnFocus:true,
40841                 width: 130,
40842                 listeners : {
40843                     'select': function(c, r, i) {
40844                         // initial support only for on class per el..
40845                         tb.selectedNode.className =  r ? r.get('val') : '';
40846                     }
40847                 }
40848     
40849             }));
40850         }
40851             
40852         
40853         
40854         for (var i in tlist) {
40855             
40856             var item = tlist[i];
40857             tb.add(item.title + ":&nbsp;");
40858             
40859             
40860             
40861             
40862             if (item.opts) {
40863                 // opts == pulldown..
40864                 tb.addField( new Roo.form.ComboBox({
40865                     store: new Roo.data.SimpleStore({
40866                         id : 'val',
40867                         fields: ['val'],
40868                         data : item.opts  
40869                     }),
40870                     name : i,
40871                     displayField:'val',
40872                     typeAhead: false,
40873                     mode: 'local',
40874                     editable : false,
40875                     triggerAction: 'all',
40876                     emptyText:'Select',
40877                     selectOnFocus:true,
40878                     width: item.width ? item.width  : 130,
40879                     listeners : {
40880                         'select': function(c, r, i) {
40881                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40882                         }
40883                     }
40884
40885                 }));
40886                 continue;
40887                     
40888                  
40889                 
40890                 tb.addField( new Roo.form.TextField({
40891                     name: i,
40892                     width: 100,
40893                     //allowBlank:false,
40894                     value: ''
40895                 }));
40896                 continue;
40897             }
40898             tb.addField( new Roo.form.TextField({
40899                 name: i,
40900                 width: item.width,
40901                 //allowBlank:true,
40902                 value: '',
40903                 listeners: {
40904                     'change' : function(f, nv, ov) {
40905                         tb.selectedNode.setAttribute(f.name, nv);
40906                     }
40907                 }
40908             }));
40909              
40910         }
40911         tb.el.on('click', function(e){
40912             e.preventDefault(); // what does this do?
40913         });
40914         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40915         tb.el.hide();
40916         tb.name = nm;
40917         // dont need to disable them... as they will get hidden
40918         return tb;
40919          
40920         
40921     },
40922     buildFooter : function()
40923     {
40924         
40925         var fel = this.editor.wrap.createChild();
40926         this.footer = new Roo.Toolbar(fel);
40927         // toolbar has scrolly on left / right?
40928         var footDisp= new Roo.Toolbar.Fill();
40929         var _t = this;
40930         this.footer.add(
40931             {
40932                 text : '&lt;',
40933                 xtype: 'Button',
40934                 handler : function() {
40935                     _t.footDisp.scrollTo('left',0,true)
40936                 }
40937             }
40938         );
40939         this.footer.add( footDisp );
40940         this.footer.add( 
40941             {
40942                 text : '&gt;',
40943                 xtype: 'Button',
40944                 handler : function() {
40945                     // no animation..
40946                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40947                 }
40948             }
40949         );
40950         var fel = Roo.get(footDisp.el);
40951         fel.addClass('x-editor-context');
40952         this.footDispWrap = fel; 
40953         this.footDispWrap.overflow  = 'hidden';
40954         
40955         this.footDisp = fel.createChild();
40956         this.footDispWrap.on('click', this.onContextClick, this)
40957         
40958         
40959     },
40960     onContextClick : function (ev,dom)
40961     {
40962         ev.preventDefault();
40963         var  cn = dom.className;
40964         Roo.log(cn);
40965         if (!cn.match(/x-ed-loc-/)) {
40966             return;
40967         }
40968         var n = cn.split('-').pop();
40969         var ans = this.footerEls;
40970         var sel = ans[n];
40971         
40972          // pick
40973         var range = this.editor.createRange();
40974         
40975         range.selectNodeContents(sel);
40976         //range.selectNode(sel);
40977         
40978         
40979         var selection = this.editor.getSelection();
40980         selection.removeAllRanges();
40981         selection.addRange(range);
40982         
40983         
40984         
40985         this.updateToolbar(null, null, sel);
40986         
40987         
40988     }
40989     
40990     
40991     
40992     
40993     
40994 });
40995
40996
40997
40998
40999
41000 /*
41001  * Based on:
41002  * Ext JS Library 1.1.1
41003  * Copyright(c) 2006-2007, Ext JS, LLC.
41004  *
41005  * Originally Released Under LGPL - original licence link has changed is not relivant.
41006  *
41007  * Fork - LGPL
41008  * <script type="text/javascript">
41009  */
41010  
41011 /**
41012  * @class Roo.form.BasicForm
41013  * @extends Roo.util.Observable
41014  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41015  * @constructor
41016  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41017  * @param {Object} config Configuration options
41018  */
41019 Roo.form.BasicForm = function(el, config){
41020     this.allItems = [];
41021     this.childForms = [];
41022     Roo.apply(this, config);
41023     /*
41024      * The Roo.form.Field items in this form.
41025      * @type MixedCollection
41026      */
41027      
41028      
41029     this.items = new Roo.util.MixedCollection(false, function(o){
41030         return o.id || (o.id = Roo.id());
41031     });
41032     this.addEvents({
41033         /**
41034          * @event beforeaction
41035          * Fires before any action is performed. Return false to cancel the action.
41036          * @param {Form} this
41037          * @param {Action} action The action to be performed
41038          */
41039         beforeaction: true,
41040         /**
41041          * @event actionfailed
41042          * Fires when an action fails.
41043          * @param {Form} this
41044          * @param {Action} action The action that failed
41045          */
41046         actionfailed : true,
41047         /**
41048          * @event actioncomplete
41049          * Fires when an action is completed.
41050          * @param {Form} this
41051          * @param {Action} action The action that completed
41052          */
41053         actioncomplete : true
41054     });
41055     if(el){
41056         this.initEl(el);
41057     }
41058     Roo.form.BasicForm.superclass.constructor.call(this);
41059 };
41060
41061 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41062     /**
41063      * @cfg {String} method
41064      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41065      */
41066     /**
41067      * @cfg {DataReader} reader
41068      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41069      * This is optional as there is built-in support for processing JSON.
41070      */
41071     /**
41072      * @cfg {DataReader} errorReader
41073      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41074      * This is completely optional as there is built-in support for processing JSON.
41075      */
41076     /**
41077      * @cfg {String} url
41078      * The URL to use for form actions if one isn't supplied in the action options.
41079      */
41080     /**
41081      * @cfg {Boolean} fileUpload
41082      * Set to true if this form is a file upload.
41083      */
41084      
41085     /**
41086      * @cfg {Object} baseParams
41087      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41088      */
41089      /**
41090      
41091     /**
41092      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41093      */
41094     timeout: 30,
41095
41096     // private
41097     activeAction : null,
41098
41099     /**
41100      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41101      * or setValues() data instead of when the form was first created.
41102      */
41103     trackResetOnLoad : false,
41104     
41105     
41106     /**
41107      * childForms - used for multi-tab forms
41108      * @type {Array}
41109      */
41110     childForms : false,
41111     
41112     /**
41113      * allItems - full list of fields.
41114      * @type {Array}
41115      */
41116     allItems : false,
41117     
41118     /**
41119      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41120      * element by passing it or its id or mask the form itself by passing in true.
41121      * @type Mixed
41122      */
41123     waitMsgTarget : false,
41124
41125     // private
41126     initEl : function(el){
41127         this.el = Roo.get(el);
41128         this.id = this.el.id || Roo.id();
41129         this.el.on('submit', this.onSubmit, this);
41130         this.el.addClass('x-form');
41131     },
41132
41133     // private
41134     onSubmit : function(e){
41135         e.stopEvent();
41136     },
41137
41138     /**
41139      * Returns true if client-side validation on the form is successful.
41140      * @return Boolean
41141      */
41142     isValid : function(){
41143         var valid = true;
41144         this.items.each(function(f){
41145            if(!f.validate()){
41146                valid = false;
41147            }
41148         });
41149         return valid;
41150     },
41151
41152     /**
41153      * Returns true if any fields in this form have changed since their original load.
41154      * @return Boolean
41155      */
41156     isDirty : function(){
41157         var dirty = false;
41158         this.items.each(function(f){
41159            if(f.isDirty()){
41160                dirty = true;
41161                return false;
41162            }
41163         });
41164         return dirty;
41165     },
41166
41167     /**
41168      * Performs a predefined action (submit or load) or custom actions you define on this form.
41169      * @param {String} actionName The name of the action type
41170      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41171      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41172      * accept other config options):
41173      * <pre>
41174 Property          Type             Description
41175 ----------------  ---------------  ----------------------------------------------------------------------------------
41176 url               String           The url for the action (defaults to the form's url)
41177 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41178 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41179 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41180                                    validate the form on the client (defaults to false)
41181      * </pre>
41182      * @return {BasicForm} this
41183      */
41184     doAction : function(action, options){
41185         if(typeof action == 'string'){
41186             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41187         }
41188         if(this.fireEvent('beforeaction', this, action) !== false){
41189             this.beforeAction(action);
41190             action.run.defer(100, action);
41191         }
41192         return this;
41193     },
41194
41195     /**
41196      * Shortcut to do a submit action.
41197      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41198      * @return {BasicForm} this
41199      */
41200     submit : function(options){
41201         this.doAction('submit', options);
41202         return this;
41203     },
41204
41205     /**
41206      * Shortcut to do a load action.
41207      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41208      * @return {BasicForm} this
41209      */
41210     load : function(options){
41211         this.doAction('load', options);
41212         return this;
41213     },
41214
41215     /**
41216      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41217      * @param {Record} record The record to edit
41218      * @return {BasicForm} this
41219      */
41220     updateRecord : function(record){
41221         record.beginEdit();
41222         var fs = record.fields;
41223         fs.each(function(f){
41224             var field = this.findField(f.name);
41225             if(field){
41226                 record.set(f.name, field.getValue());
41227             }
41228         }, this);
41229         record.endEdit();
41230         return this;
41231     },
41232
41233     /**
41234      * Loads an Roo.data.Record into this form.
41235      * @param {Record} record The record to load
41236      * @return {BasicForm} this
41237      */
41238     loadRecord : function(record){
41239         this.setValues(record.data);
41240         return this;
41241     },
41242
41243     // private
41244     beforeAction : function(action){
41245         var o = action.options;
41246         
41247        
41248         if(this.waitMsgTarget === true){
41249             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41250         }else if(this.waitMsgTarget){
41251             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41252             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41253         }else {
41254             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41255         }
41256          
41257     },
41258
41259     // private
41260     afterAction : function(action, success){
41261         this.activeAction = null;
41262         var o = action.options;
41263         
41264         if(this.waitMsgTarget === true){
41265             this.el.unmask();
41266         }else if(this.waitMsgTarget){
41267             this.waitMsgTarget.unmask();
41268         }else{
41269             Roo.MessageBox.updateProgress(1);
41270             Roo.MessageBox.hide();
41271         }
41272          
41273         if(success){
41274             if(o.reset){
41275                 this.reset();
41276             }
41277             Roo.callback(o.success, o.scope, [this, action]);
41278             this.fireEvent('actioncomplete', this, action);
41279             
41280         }else{
41281             Roo.callback(o.failure, o.scope, [this, action]);
41282             // show an error message if no failed handler is set..
41283             if (!this.hasListener('actionfailed')) {
41284                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
41285             }
41286             
41287             this.fireEvent('actionfailed', this, action);
41288         }
41289         
41290     },
41291
41292     /**
41293      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41294      * @param {String} id The value to search for
41295      * @return Field
41296      */
41297     findField : function(id){
41298         var field = this.items.get(id);
41299         if(!field){
41300             this.items.each(function(f){
41301                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41302                     field = f;
41303                     return false;
41304                 }
41305             });
41306         }
41307         return field || null;
41308     },
41309
41310     /**
41311      * Add a secondary form to this one, 
41312      * Used to provide tabbed forms. One form is primary, with hidden values 
41313      * which mirror the elements from the other forms.
41314      * 
41315      * @param {Roo.form.Form} form to add.
41316      * 
41317      */
41318     addForm : function(form)
41319     {
41320        
41321         if (this.childForms.indexOf(form) > -1) {
41322             // already added..
41323             return;
41324         }
41325         this.childForms.push(form);
41326         var n = '';
41327         Roo.each(form.allItems, function (fe) {
41328             
41329             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41330             if (this.findField(n)) { // already added..
41331                 return;
41332             }
41333             var add = new Roo.form.Hidden({
41334                 name : n
41335             });
41336             add.render(this.el);
41337             
41338             this.add( add );
41339         }, this);
41340         
41341     },
41342     /**
41343      * Mark fields in this form invalid in bulk.
41344      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41345      * @return {BasicForm} this
41346      */
41347     markInvalid : function(errors){
41348         if(errors instanceof Array){
41349             for(var i = 0, len = errors.length; i < len; i++){
41350                 var fieldError = errors[i];
41351                 var f = this.findField(fieldError.id);
41352                 if(f){
41353                     f.markInvalid(fieldError.msg);
41354                 }
41355             }
41356         }else{
41357             var field, id;
41358             for(id in errors){
41359                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41360                     field.markInvalid(errors[id]);
41361                 }
41362             }
41363         }
41364         Roo.each(this.childForms || [], function (f) {
41365             f.markInvalid(errors);
41366         });
41367         
41368         return this;
41369     },
41370
41371     /**
41372      * Set values for fields in this form in bulk.
41373      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41374      * @return {BasicForm} this
41375      */
41376     setValues : function(values){
41377         if(values instanceof Array){ // array of objects
41378             for(var i = 0, len = values.length; i < len; i++){
41379                 var v = values[i];
41380                 var f = this.findField(v.id);
41381                 if(f){
41382                     f.setValue(v.value);
41383                     if(this.trackResetOnLoad){
41384                         f.originalValue = f.getValue();
41385                     }
41386                 }
41387             }
41388         }else{ // object hash
41389             var field, id;
41390             for(id in values){
41391                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41392                     
41393                     if (field.setFromData && 
41394                         field.valueField && 
41395                         field.displayField &&
41396                         // combos' with local stores can 
41397                         // be queried via setValue()
41398                         // to set their value..
41399                         (field.store && !field.store.isLocal)
41400                         ) {
41401                         // it's a combo
41402                         var sd = { };
41403                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41404                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41405                         field.setFromData(sd);
41406                         
41407                     } else {
41408                         field.setValue(values[id]);
41409                     }
41410                     
41411                     
41412                     if(this.trackResetOnLoad){
41413                         field.originalValue = field.getValue();
41414                     }
41415                 }
41416             }
41417         }
41418          
41419         Roo.each(this.childForms || [], function (f) {
41420             f.setValues(values);
41421         });
41422                 
41423         return this;
41424     },
41425
41426     /**
41427      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41428      * they are returned as an array.
41429      * @param {Boolean} asString
41430      * @return {Object}
41431      */
41432     getValues : function(asString){
41433         if (this.childForms) {
41434             // copy values from the child forms
41435             Roo.each(this.childForms, function (f) {
41436                 this.setValues(f.getValues());
41437             }, this);
41438         }
41439         
41440         
41441         
41442         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41443         if(asString === true){
41444             return fs;
41445         }
41446         return Roo.urlDecode(fs);
41447     },
41448     
41449     /**
41450      * Returns the fields in this form as an object with key/value pairs. 
41451      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41452      * @return {Object}
41453      */
41454     getFieldValues : function()
41455     {
41456         if (this.childForms) {
41457             // copy values from the child forms
41458             Roo.each(this.childForms, function (f) {
41459                 this.setValues(f.getValues());
41460             }, this);
41461         }
41462         
41463         var ret = {};
41464         this.items.each(function(f){
41465             if (!f.getName()) {
41466                 return;
41467             }
41468             var v = f.getValue();
41469             if ((typeof(v) == 'object') && f.getRawValue) {
41470                 v = f.getRawValue() ; // dates..
41471             }
41472             ret[f.getName()] = v;
41473         });
41474         
41475         return ret;
41476     },
41477
41478     /**
41479      * Clears all invalid messages in this form.
41480      * @return {BasicForm} this
41481      */
41482     clearInvalid : function(){
41483         this.items.each(function(f){
41484            f.clearInvalid();
41485         });
41486         
41487         Roo.each(this.childForms || [], function (f) {
41488             f.clearInvalid();
41489         });
41490         
41491         
41492         return this;
41493     },
41494
41495     /**
41496      * Resets this form.
41497      * @return {BasicForm} this
41498      */
41499     reset : function(){
41500         this.items.each(function(f){
41501             f.reset();
41502         });
41503         
41504         Roo.each(this.childForms || [], function (f) {
41505             f.reset();
41506         });
41507        
41508         
41509         return this;
41510     },
41511
41512     /**
41513      * Add Roo.form components to this form.
41514      * @param {Field} field1
41515      * @param {Field} field2 (optional)
41516      * @param {Field} etc (optional)
41517      * @return {BasicForm} this
41518      */
41519     add : function(){
41520         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41521         return this;
41522     },
41523
41524
41525     /**
41526      * Removes a field from the items collection (does NOT remove its markup).
41527      * @param {Field} field
41528      * @return {BasicForm} this
41529      */
41530     remove : function(field){
41531         this.items.remove(field);
41532         return this;
41533     },
41534
41535     /**
41536      * Looks at the fields in this form, checks them for an id attribute,
41537      * and calls applyTo on the existing dom element with that id.
41538      * @return {BasicForm} this
41539      */
41540     render : function(){
41541         this.items.each(function(f){
41542             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41543                 f.applyTo(f.id);
41544             }
41545         });
41546         return this;
41547     },
41548
41549     /**
41550      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41551      * @param {Object} values
41552      * @return {BasicForm} this
41553      */
41554     applyToFields : function(o){
41555         this.items.each(function(f){
41556            Roo.apply(f, o);
41557         });
41558         return this;
41559     },
41560
41561     /**
41562      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41563      * @param {Object} values
41564      * @return {BasicForm} this
41565      */
41566     applyIfToFields : function(o){
41567         this.items.each(function(f){
41568            Roo.applyIf(f, o);
41569         });
41570         return this;
41571     }
41572 });
41573
41574 // back compat
41575 Roo.BasicForm = Roo.form.BasicForm;/*
41576  * Based on:
41577  * Ext JS Library 1.1.1
41578  * Copyright(c) 2006-2007, Ext JS, LLC.
41579  *
41580  * Originally Released Under LGPL - original licence link has changed is not relivant.
41581  *
41582  * Fork - LGPL
41583  * <script type="text/javascript">
41584  */
41585
41586 /**
41587  * @class Roo.form.Form
41588  * @extends Roo.form.BasicForm
41589  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41590  * @constructor
41591  * @param {Object} config Configuration options
41592  */
41593 Roo.form.Form = function(config){
41594     var xitems =  [];
41595     if (config.items) {
41596         xitems = config.items;
41597         delete config.items;
41598     }
41599    
41600     
41601     Roo.form.Form.superclass.constructor.call(this, null, config);
41602     this.url = this.url || this.action;
41603     if(!this.root){
41604         this.root = new Roo.form.Layout(Roo.applyIf({
41605             id: Roo.id()
41606         }, config));
41607     }
41608     this.active = this.root;
41609     /**
41610      * Array of all the buttons that have been added to this form via {@link addButton}
41611      * @type Array
41612      */
41613     this.buttons = [];
41614     this.allItems = [];
41615     this.addEvents({
41616         /**
41617          * @event clientvalidation
41618          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41619          * @param {Form} this
41620          * @param {Boolean} valid true if the form has passed client-side validation
41621          */
41622         clientvalidation: true,
41623         /**
41624          * @event rendered
41625          * Fires when the form is rendered
41626          * @param {Roo.form.Form} form
41627          */
41628         rendered : true
41629     });
41630     
41631     if (this.progressUrl) {
41632             // push a hidden field onto the list of fields..
41633             this.addxtype( {
41634                     xns: Roo.form, 
41635                     xtype : 'Hidden', 
41636                     name : 'UPLOAD_IDENTIFIER' 
41637             });
41638         }
41639         
41640     
41641     Roo.each(xitems, this.addxtype, this);
41642     
41643     
41644     
41645 };
41646
41647 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41648     /**
41649      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41650      */
41651     /**
41652      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41653      */
41654     /**
41655      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41656      */
41657     buttonAlign:'center',
41658
41659     /**
41660      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41661      */
41662     minButtonWidth:75,
41663
41664     /**
41665      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41666      * This property cascades to child containers if not set.
41667      */
41668     labelAlign:'left',
41669
41670     /**
41671      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41672      * fires a looping event with that state. This is required to bind buttons to the valid
41673      * state using the config value formBind:true on the button.
41674      */
41675     monitorValid : false,
41676
41677     /**
41678      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41679      */
41680     monitorPoll : 200,
41681     
41682     /**
41683      * @cfg {String} progressUrl - Url to return progress data 
41684      */
41685     
41686     progressUrl : false,
41687   
41688     /**
41689      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41690      * fields are added and the column is closed. If no fields are passed the column remains open
41691      * until end() is called.
41692      * @param {Object} config The config to pass to the column
41693      * @param {Field} field1 (optional)
41694      * @param {Field} field2 (optional)
41695      * @param {Field} etc (optional)
41696      * @return Column The column container object
41697      */
41698     column : function(c){
41699         var col = new Roo.form.Column(c);
41700         this.start(col);
41701         if(arguments.length > 1){ // duplicate code required because of Opera
41702             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41703             this.end();
41704         }
41705         return col;
41706     },
41707
41708     /**
41709      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41710      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41711      * until end() is called.
41712      * @param {Object} config The config to pass to the fieldset
41713      * @param {Field} field1 (optional)
41714      * @param {Field} field2 (optional)
41715      * @param {Field} etc (optional)
41716      * @return FieldSet The fieldset container object
41717      */
41718     fieldset : function(c){
41719         var fs = new Roo.form.FieldSet(c);
41720         this.start(fs);
41721         if(arguments.length > 1){ // duplicate code required because of Opera
41722             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41723             this.end();
41724         }
41725         return fs;
41726     },
41727
41728     /**
41729      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41730      * fields are added and the container is closed. If no fields are passed the container remains open
41731      * until end() is called.
41732      * @param {Object} config The config to pass to the Layout
41733      * @param {Field} field1 (optional)
41734      * @param {Field} field2 (optional)
41735      * @param {Field} etc (optional)
41736      * @return Layout The container object
41737      */
41738     container : function(c){
41739         var l = new Roo.form.Layout(c);
41740         this.start(l);
41741         if(arguments.length > 1){ // duplicate code required because of Opera
41742             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41743             this.end();
41744         }
41745         return l;
41746     },
41747
41748     /**
41749      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41750      * @param {Object} container A Roo.form.Layout or subclass of Layout
41751      * @return {Form} this
41752      */
41753     start : function(c){
41754         // cascade label info
41755         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41756         this.active.stack.push(c);
41757         c.ownerCt = this.active;
41758         this.active = c;
41759         return this;
41760     },
41761
41762     /**
41763      * Closes the current open container
41764      * @return {Form} this
41765      */
41766     end : function(){
41767         if(this.active == this.root){
41768             return this;
41769         }
41770         this.active = this.active.ownerCt;
41771         return this;
41772     },
41773
41774     /**
41775      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41776      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41777      * as the label of the field.
41778      * @param {Field} field1
41779      * @param {Field} field2 (optional)
41780      * @param {Field} etc. (optional)
41781      * @return {Form} this
41782      */
41783     add : function(){
41784         this.active.stack.push.apply(this.active.stack, arguments);
41785         this.allItems.push.apply(this.allItems,arguments);
41786         var r = [];
41787         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41788             if(a[i].isFormField){
41789                 r.push(a[i]);
41790             }
41791         }
41792         if(r.length > 0){
41793             Roo.form.Form.superclass.add.apply(this, r);
41794         }
41795         return this;
41796     },
41797     
41798
41799     
41800     
41801     
41802      /**
41803      * Find any element that has been added to a form, using it's ID or name
41804      * This can include framesets, columns etc. along with regular fields..
41805      * @param {String} id - id or name to find.
41806      
41807      * @return {Element} e - or false if nothing found.
41808      */
41809     findbyId : function(id)
41810     {
41811         var ret = false;
41812         if (!id) {
41813             return ret;
41814         }
41815         Roo.each(this.allItems, function(f){
41816             if (f.id == id || f.name == id ){
41817                 ret = f;
41818                 return false;
41819             }
41820         });
41821         return ret;
41822     },
41823
41824     
41825     
41826     /**
41827      * Render this form into the passed container. This should only be called once!
41828      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41829      * @return {Form} this
41830      */
41831     render : function(ct)
41832     {
41833         
41834         
41835         
41836         ct = Roo.get(ct);
41837         var o = this.autoCreate || {
41838             tag: 'form',
41839             method : this.method || 'POST',
41840             id : this.id || Roo.id()
41841         };
41842         this.initEl(ct.createChild(o));
41843
41844         this.root.render(this.el);
41845         
41846        
41847              
41848         this.items.each(function(f){
41849             f.render('x-form-el-'+f.id);
41850         });
41851
41852         if(this.buttons.length > 0){
41853             // tables are required to maintain order and for correct IE layout
41854             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41855                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41856                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41857             }}, null, true);
41858             var tr = tb.getElementsByTagName('tr')[0];
41859             for(var i = 0, len = this.buttons.length; i < len; i++) {
41860                 var b = this.buttons[i];
41861                 var td = document.createElement('td');
41862                 td.className = 'x-form-btn-td';
41863                 b.render(tr.appendChild(td));
41864             }
41865         }
41866         if(this.monitorValid){ // initialize after render
41867             this.startMonitoring();
41868         }
41869         this.fireEvent('rendered', this);
41870         return this;
41871     },
41872
41873     /**
41874      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41875      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41876      * object or a valid Roo.DomHelper element config
41877      * @param {Function} handler The function called when the button is clicked
41878      * @param {Object} scope (optional) The scope of the handler function
41879      * @return {Roo.Button}
41880      */
41881     addButton : function(config, handler, scope){
41882         var bc = {
41883             handler: handler,
41884             scope: scope,
41885             minWidth: this.minButtonWidth,
41886             hideParent:true
41887         };
41888         if(typeof config == "string"){
41889             bc.text = config;
41890         }else{
41891             Roo.apply(bc, config);
41892         }
41893         var btn = new Roo.Button(null, bc);
41894         this.buttons.push(btn);
41895         return btn;
41896     },
41897
41898      /**
41899      * Adds a series of form elements (using the xtype property as the factory method.
41900      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41901      * @param {Object} config 
41902      */
41903     
41904     addxtype : function()
41905     {
41906         var ar = Array.prototype.slice.call(arguments, 0);
41907         var ret = false;
41908         for(var i = 0; i < ar.length; i++) {
41909             if (!ar[i]) {
41910                 continue; // skip -- if this happends something invalid got sent, we 
41911                 // should ignore it, as basically that interface element will not show up
41912                 // and that should be pretty obvious!!
41913             }
41914             
41915             if (Roo.form[ar[i].xtype]) {
41916                 ar[i].form = this;
41917                 var fe = Roo.factory(ar[i], Roo.form);
41918                 if (!ret) {
41919                     ret = fe;
41920                 }
41921                 fe.form = this;
41922                 if (fe.store) {
41923                     fe.store.form = this;
41924                 }
41925                 if (fe.isLayout) {  
41926                          
41927                     this.start(fe);
41928                     this.allItems.push(fe);
41929                     if (fe.items && fe.addxtype) {
41930                         fe.addxtype.apply(fe, fe.items);
41931                         delete fe.items;
41932                     }
41933                      this.end();
41934                     continue;
41935                 }
41936                 
41937                 
41938                  
41939                 this.add(fe);
41940               //  console.log('adding ' + ar[i].xtype);
41941             }
41942             if (ar[i].xtype == 'Button') {  
41943                 //console.log('adding button');
41944                 //console.log(ar[i]);
41945                 this.addButton(ar[i]);
41946                 this.allItems.push(fe);
41947                 continue;
41948             }
41949             
41950             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41951                 alert('end is not supported on xtype any more, use items');
41952             //    this.end();
41953             //    //console.log('adding end');
41954             }
41955             
41956         }
41957         return ret;
41958     },
41959     
41960     /**
41961      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41962      * option "monitorValid"
41963      */
41964     startMonitoring : function(){
41965         if(!this.bound){
41966             this.bound = true;
41967             Roo.TaskMgr.start({
41968                 run : this.bindHandler,
41969                 interval : this.monitorPoll || 200,
41970                 scope: this
41971             });
41972         }
41973     },
41974
41975     /**
41976      * Stops monitoring of the valid state of this form
41977      */
41978     stopMonitoring : function(){
41979         this.bound = false;
41980     },
41981
41982     // private
41983     bindHandler : function(){
41984         if(!this.bound){
41985             return false; // stops binding
41986         }
41987         var valid = true;
41988         this.items.each(function(f){
41989             if(!f.isValid(true)){
41990                 valid = false;
41991                 return false;
41992             }
41993         });
41994         for(var i = 0, len = this.buttons.length; i < len; i++){
41995             var btn = this.buttons[i];
41996             if(btn.formBind === true && btn.disabled === valid){
41997                 btn.setDisabled(!valid);
41998             }
41999         }
42000         this.fireEvent('clientvalidation', this, valid);
42001     }
42002     
42003     
42004     
42005     
42006     
42007     
42008     
42009     
42010 });
42011
42012
42013 // back compat
42014 Roo.Form = Roo.form.Form;
42015 /*
42016  * Based on:
42017  * Ext JS Library 1.1.1
42018  * Copyright(c) 2006-2007, Ext JS, LLC.
42019  *
42020  * Originally Released Under LGPL - original licence link has changed is not relivant.
42021  *
42022  * Fork - LGPL
42023  * <script type="text/javascript">
42024  */
42025  
42026  /**
42027  * @class Roo.form.Action
42028  * Internal Class used to handle form actions
42029  * @constructor
42030  * @param {Roo.form.BasicForm} el The form element or its id
42031  * @param {Object} config Configuration options
42032  */
42033  
42034  
42035 // define the action interface
42036 Roo.form.Action = function(form, options){
42037     this.form = form;
42038     this.options = options || {};
42039 };
42040 /**
42041  * Client Validation Failed
42042  * @const 
42043  */
42044 Roo.form.Action.CLIENT_INVALID = 'client';
42045 /**
42046  * Server Validation Failed
42047  * @const 
42048  */
42049  Roo.form.Action.SERVER_INVALID = 'server';
42050  /**
42051  * Connect to Server Failed
42052  * @const 
42053  */
42054 Roo.form.Action.CONNECT_FAILURE = 'connect';
42055 /**
42056  * Reading Data from Server Failed
42057  * @const 
42058  */
42059 Roo.form.Action.LOAD_FAILURE = 'load';
42060
42061 Roo.form.Action.prototype = {
42062     type : 'default',
42063     failureType : undefined,
42064     response : undefined,
42065     result : undefined,
42066
42067     // interface method
42068     run : function(options){
42069
42070     },
42071
42072     // interface method
42073     success : function(response){
42074
42075     },
42076
42077     // interface method
42078     handleResponse : function(response){
42079
42080     },
42081
42082     // default connection failure
42083     failure : function(response){
42084         
42085         this.response = response;
42086         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42087         this.form.afterAction(this, false);
42088     },
42089
42090     processResponse : function(response){
42091         this.response = response;
42092         if(!response.responseText){
42093             return true;
42094         }
42095         this.result = this.handleResponse(response);
42096         return this.result;
42097     },
42098
42099     // utility functions used internally
42100     getUrl : function(appendParams){
42101         var url = this.options.url || this.form.url || this.form.el.dom.action;
42102         if(appendParams){
42103             var p = this.getParams();
42104             if(p){
42105                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42106             }
42107         }
42108         return url;
42109     },
42110
42111     getMethod : function(){
42112         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42113     },
42114
42115     getParams : function(){
42116         var bp = this.form.baseParams;
42117         var p = this.options.params;
42118         if(p){
42119             if(typeof p == "object"){
42120                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42121             }else if(typeof p == 'string' && bp){
42122                 p += '&' + Roo.urlEncode(bp);
42123             }
42124         }else if(bp){
42125             p = Roo.urlEncode(bp);
42126         }
42127         return p;
42128     },
42129
42130     createCallback : function(){
42131         return {
42132             success: this.success,
42133             failure: this.failure,
42134             scope: this,
42135             timeout: (this.form.timeout*1000),
42136             upload: this.form.fileUpload ? this.success : undefined
42137         };
42138     }
42139 };
42140
42141 Roo.form.Action.Submit = function(form, options){
42142     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42143 };
42144
42145 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42146     type : 'submit',
42147
42148     haveProgress : false,
42149     uploadComplete : false,
42150     
42151     // uploadProgress indicator.
42152     uploadProgress : function()
42153     {
42154         if (!this.form.progressUrl) {
42155             return;
42156         }
42157         
42158         if (!this.haveProgress) {
42159             Roo.MessageBox.progress("Uploading", "Uploading");
42160         }
42161         if (this.uploadComplete) {
42162            Roo.MessageBox.hide();
42163            return;
42164         }
42165         
42166         this.haveProgress = true;
42167    
42168         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42169         
42170         var c = new Roo.data.Connection();
42171         c.request({
42172             url : this.form.progressUrl,
42173             params: {
42174                 id : uid
42175             },
42176             method: 'GET',
42177             success : function(req){
42178                //console.log(data);
42179                 var rdata = false;
42180                 var edata;
42181                 try  {
42182                    rdata = Roo.decode(req.responseText)
42183                 } catch (e) {
42184                     Roo.log("Invalid data from server..");
42185                     Roo.log(edata);
42186                     return;
42187                 }
42188                 if (!rdata || !rdata.success) {
42189                     Roo.log(rdata);
42190                     return;
42191                 }
42192                 var data = rdata.data;
42193                 
42194                 if (this.uploadComplete) {
42195                    Roo.MessageBox.hide();
42196                    return;
42197                 }
42198                    
42199                 if (data){
42200                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42201                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42202                     );
42203                 }
42204                 this.uploadProgress.defer(2000,this);
42205             },
42206        
42207             failure: function(data) {
42208                 Roo.log('progress url failed ');
42209                 Roo.log(data);
42210             },
42211             scope : this
42212         });
42213            
42214     },
42215     
42216     
42217     run : function()
42218     {
42219         // run get Values on the form, so it syncs any secondary forms.
42220         this.form.getValues();
42221         
42222         var o = this.options;
42223         var method = this.getMethod();
42224         var isPost = method == 'POST';
42225         if(o.clientValidation === false || this.form.isValid()){
42226             
42227             if (this.form.progressUrl) {
42228                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42229                     (new Date() * 1) + '' + Math.random());
42230                     
42231             } 
42232             
42233             
42234             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42235                 form:this.form.el.dom,
42236                 url:this.getUrl(!isPost),
42237                 method: method,
42238                 params:isPost ? this.getParams() : null,
42239                 isUpload: this.form.fileUpload
42240             }));
42241             
42242             this.uploadProgress();
42243
42244         }else if (o.clientValidation !== false){ // client validation failed
42245             this.failureType = Roo.form.Action.CLIENT_INVALID;
42246             this.form.afterAction(this, false);
42247         }
42248     },
42249
42250     success : function(response)
42251     {
42252         this.uploadComplete= true;
42253         if (this.haveProgress) {
42254             Roo.MessageBox.hide();
42255         }
42256         
42257         
42258         var result = this.processResponse(response);
42259         if(result === true || result.success){
42260             this.form.afterAction(this, true);
42261             return;
42262         }
42263         if(result.errors){
42264             this.form.markInvalid(result.errors);
42265             this.failureType = Roo.form.Action.SERVER_INVALID;
42266         }
42267         this.form.afterAction(this, false);
42268     },
42269     failure : function(response)
42270     {
42271         this.uploadComplete= true;
42272         if (this.haveProgress) {
42273             Roo.MessageBox.hide();
42274         }
42275         
42276         
42277         this.response = response;
42278         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42279         this.form.afterAction(this, false);
42280     },
42281     
42282     handleResponse : function(response){
42283         if(this.form.errorReader){
42284             var rs = this.form.errorReader.read(response);
42285             var errors = [];
42286             if(rs.records){
42287                 for(var i = 0, len = rs.records.length; i < len; i++) {
42288                     var r = rs.records[i];
42289                     errors[i] = r.data;
42290                 }
42291             }
42292             if(errors.length < 1){
42293                 errors = null;
42294             }
42295             return {
42296                 success : rs.success,
42297                 errors : errors
42298             };
42299         }
42300         var ret = false;
42301         try {
42302             ret = Roo.decode(response.responseText);
42303         } catch (e) {
42304             ret = {
42305                 success: false,
42306                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42307                 errors : []
42308             };
42309         }
42310         return ret;
42311         
42312     }
42313 });
42314
42315
42316 Roo.form.Action.Load = function(form, options){
42317     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42318     this.reader = this.form.reader;
42319 };
42320
42321 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42322     type : 'load',
42323
42324     run : function(){
42325         
42326         Roo.Ajax.request(Roo.apply(
42327                 this.createCallback(), {
42328                     method:this.getMethod(),
42329                     url:this.getUrl(false),
42330                     params:this.getParams()
42331         }));
42332     },
42333
42334     success : function(response){
42335         
42336         var result = this.processResponse(response);
42337         if(result === true || !result.success || !result.data){
42338             this.failureType = Roo.form.Action.LOAD_FAILURE;
42339             this.form.afterAction(this, false);
42340             return;
42341         }
42342         this.form.clearInvalid();
42343         this.form.setValues(result.data);
42344         this.form.afterAction(this, true);
42345     },
42346
42347     handleResponse : function(response){
42348         if(this.form.reader){
42349             var rs = this.form.reader.read(response);
42350             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42351             return {
42352                 success : rs.success,
42353                 data : data
42354             };
42355         }
42356         return Roo.decode(response.responseText);
42357     }
42358 });
42359
42360 Roo.form.Action.ACTION_TYPES = {
42361     'load' : Roo.form.Action.Load,
42362     'submit' : Roo.form.Action.Submit
42363 };/*
42364  * Based on:
42365  * Ext JS Library 1.1.1
42366  * Copyright(c) 2006-2007, Ext JS, LLC.
42367  *
42368  * Originally Released Under LGPL - original licence link has changed is not relivant.
42369  *
42370  * Fork - LGPL
42371  * <script type="text/javascript">
42372  */
42373  
42374 /**
42375  * @class Roo.form.Layout
42376  * @extends Roo.Component
42377  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42378  * @constructor
42379  * @param {Object} config Configuration options
42380  */
42381 Roo.form.Layout = function(config){
42382     var xitems = [];
42383     if (config.items) {
42384         xitems = config.items;
42385         delete config.items;
42386     }
42387     Roo.form.Layout.superclass.constructor.call(this, config);
42388     this.stack = [];
42389     Roo.each(xitems, this.addxtype, this);
42390      
42391 };
42392
42393 Roo.extend(Roo.form.Layout, Roo.Component, {
42394     /**
42395      * @cfg {String/Object} autoCreate
42396      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42397      */
42398     /**
42399      * @cfg {String/Object/Function} style
42400      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42401      * a function which returns such a specification.
42402      */
42403     /**
42404      * @cfg {String} labelAlign
42405      * Valid values are "left," "top" and "right" (defaults to "left")
42406      */
42407     /**
42408      * @cfg {Number} labelWidth
42409      * Fixed width in pixels of all field labels (defaults to undefined)
42410      */
42411     /**
42412      * @cfg {Boolean} clear
42413      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42414      */
42415     clear : true,
42416     /**
42417      * @cfg {String} labelSeparator
42418      * The separator to use after field labels (defaults to ':')
42419      */
42420     labelSeparator : ':',
42421     /**
42422      * @cfg {Boolean} hideLabels
42423      * True to suppress the display of field labels in this layout (defaults to false)
42424      */
42425     hideLabels : false,
42426
42427     // private
42428     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42429     
42430     isLayout : true,
42431     
42432     // private
42433     onRender : function(ct, position){
42434         if(this.el){ // from markup
42435             this.el = Roo.get(this.el);
42436         }else {  // generate
42437             var cfg = this.getAutoCreate();
42438             this.el = ct.createChild(cfg, position);
42439         }
42440         if(this.style){
42441             this.el.applyStyles(this.style);
42442         }
42443         if(this.labelAlign){
42444             this.el.addClass('x-form-label-'+this.labelAlign);
42445         }
42446         if(this.hideLabels){
42447             this.labelStyle = "display:none";
42448             this.elementStyle = "padding-left:0;";
42449         }else{
42450             if(typeof this.labelWidth == 'number'){
42451                 this.labelStyle = "width:"+this.labelWidth+"px;";
42452                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42453             }
42454             if(this.labelAlign == 'top'){
42455                 this.labelStyle = "width:auto;";
42456                 this.elementStyle = "padding-left:0;";
42457             }
42458         }
42459         var stack = this.stack;
42460         var slen = stack.length;
42461         if(slen > 0){
42462             if(!this.fieldTpl){
42463                 var t = new Roo.Template(
42464                     '<div class="x-form-item {5}">',
42465                         '<label for="{0}" style="{2}">{1}{4}</label>',
42466                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42467                         '</div>',
42468                     '</div><div class="x-form-clear-left"></div>'
42469                 );
42470                 t.disableFormats = true;
42471                 t.compile();
42472                 Roo.form.Layout.prototype.fieldTpl = t;
42473             }
42474             for(var i = 0; i < slen; i++) {
42475                 if(stack[i].isFormField){
42476                     this.renderField(stack[i]);
42477                 }else{
42478                     this.renderComponent(stack[i]);
42479                 }
42480             }
42481         }
42482         if(this.clear){
42483             this.el.createChild({cls:'x-form-clear'});
42484         }
42485     },
42486
42487     // private
42488     renderField : function(f){
42489         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42490                f.id, //0
42491                f.fieldLabel, //1
42492                f.labelStyle||this.labelStyle||'', //2
42493                this.elementStyle||'', //3
42494                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42495                f.itemCls||this.itemCls||''  //5
42496        ], true).getPrevSibling());
42497     },
42498
42499     // private
42500     renderComponent : function(c){
42501         c.render(c.isLayout ? this.el : this.el.createChild());    
42502     },
42503     /**
42504      * Adds a object form elements (using the xtype property as the factory method.)
42505      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42506      * @param {Object} config 
42507      */
42508     addxtype : function(o)
42509     {
42510         // create the lement.
42511         o.form = this.form;
42512         var fe = Roo.factory(o, Roo.form);
42513         this.form.allItems.push(fe);
42514         this.stack.push(fe);
42515         
42516         if (fe.isFormField) {
42517             this.form.items.add(fe);
42518         }
42519          
42520         return fe;
42521     }
42522 });
42523
42524 /**
42525  * @class Roo.form.Column
42526  * @extends Roo.form.Layout
42527  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42528  * @constructor
42529  * @param {Object} config Configuration options
42530  */
42531 Roo.form.Column = function(config){
42532     Roo.form.Column.superclass.constructor.call(this, config);
42533 };
42534
42535 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42536     /**
42537      * @cfg {Number/String} width
42538      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42539      */
42540     /**
42541      * @cfg {String/Object} autoCreate
42542      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42543      */
42544
42545     // private
42546     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42547
42548     // private
42549     onRender : function(ct, position){
42550         Roo.form.Column.superclass.onRender.call(this, ct, position);
42551         if(this.width){
42552             this.el.setWidth(this.width);
42553         }
42554     }
42555 });
42556
42557
42558 /**
42559  * @class Roo.form.Row
42560  * @extends Roo.form.Layout
42561  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42562  * @constructor
42563  * @param {Object} config Configuration options
42564  */
42565
42566  
42567 Roo.form.Row = function(config){
42568     Roo.form.Row.superclass.constructor.call(this, config);
42569 };
42570  
42571 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42572       /**
42573      * @cfg {Number/String} width
42574      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42575      */
42576     /**
42577      * @cfg {Number/String} height
42578      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42579      */
42580     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42581     
42582     padWidth : 20,
42583     // private
42584     onRender : function(ct, position){
42585         //console.log('row render');
42586         if(!this.rowTpl){
42587             var t = new Roo.Template(
42588                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42589                     '<label for="{0}" style="{2}">{1}{4}</label>',
42590                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42591                     '</div>',
42592                 '</div>'
42593             );
42594             t.disableFormats = true;
42595             t.compile();
42596             Roo.form.Layout.prototype.rowTpl = t;
42597         }
42598         this.fieldTpl = this.rowTpl;
42599         
42600         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42601         var labelWidth = 100;
42602         
42603         if ((this.labelAlign != 'top')) {
42604             if (typeof this.labelWidth == 'number') {
42605                 labelWidth = this.labelWidth
42606             }
42607             this.padWidth =  20 + labelWidth;
42608             
42609         }
42610         
42611         Roo.form.Column.superclass.onRender.call(this, ct, position);
42612         if(this.width){
42613             this.el.setWidth(this.width);
42614         }
42615         if(this.height){
42616             this.el.setHeight(this.height);
42617         }
42618     },
42619     
42620     // private
42621     renderField : function(f){
42622         f.fieldEl = this.fieldTpl.append(this.el, [
42623                f.id, f.fieldLabel,
42624                f.labelStyle||this.labelStyle||'',
42625                this.elementStyle||'',
42626                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42627                f.itemCls||this.itemCls||'',
42628                f.width ? f.width + this.padWidth : 160 + this.padWidth
42629        ],true);
42630     }
42631 });
42632  
42633
42634 /**
42635  * @class Roo.form.FieldSet
42636  * @extends Roo.form.Layout
42637  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42638  * @constructor
42639  * @param {Object} config Configuration options
42640  */
42641 Roo.form.FieldSet = function(config){
42642     Roo.form.FieldSet.superclass.constructor.call(this, config);
42643 };
42644
42645 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42646     /**
42647      * @cfg {String} legend
42648      * The text to display as the legend for the FieldSet (defaults to '')
42649      */
42650     /**
42651      * @cfg {String/Object} autoCreate
42652      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42653      */
42654
42655     // private
42656     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42657
42658     // private
42659     onRender : function(ct, position){
42660         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42661         if(this.legend){
42662             this.setLegend(this.legend);
42663         }
42664     },
42665
42666     // private
42667     setLegend : function(text){
42668         if(this.rendered){
42669             this.el.child('legend').update(text);
42670         }
42671     }
42672 });/*
42673  * Based on:
42674  * Ext JS Library 1.1.1
42675  * Copyright(c) 2006-2007, Ext JS, LLC.
42676  *
42677  * Originally Released Under LGPL - original licence link has changed is not relivant.
42678  *
42679  * Fork - LGPL
42680  * <script type="text/javascript">
42681  */
42682 /**
42683  * @class Roo.form.VTypes
42684  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42685  * @singleton
42686  */
42687 Roo.form.VTypes = function(){
42688     // closure these in so they are only created once.
42689     var alpha = /^[a-zA-Z_]+$/;
42690     var alphanum = /^[a-zA-Z0-9_]+$/;
42691     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42692     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42693
42694     // All these messages and functions are configurable
42695     return {
42696         /**
42697          * The function used to validate email addresses
42698          * @param {String} value The email address
42699          */
42700         'email' : function(v){
42701             return email.test(v);
42702         },
42703         /**
42704          * The error text to display when the email validation function returns false
42705          * @type String
42706          */
42707         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42708         /**
42709          * The keystroke filter mask to be applied on email input
42710          * @type RegExp
42711          */
42712         'emailMask' : /[a-z0-9_\.\-@]/i,
42713
42714         /**
42715          * The function used to validate URLs
42716          * @param {String} value The URL
42717          */
42718         'url' : function(v){
42719             return url.test(v);
42720         },
42721         /**
42722          * The error text to display when the url validation function returns false
42723          * @type String
42724          */
42725         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42726         
42727         /**
42728          * The function used to validate alpha values
42729          * @param {String} value The value
42730          */
42731         'alpha' : function(v){
42732             return alpha.test(v);
42733         },
42734         /**
42735          * The error text to display when the alpha validation function returns false
42736          * @type String
42737          */
42738         'alphaText' : 'This field should only contain letters and _',
42739         /**
42740          * The keystroke filter mask to be applied on alpha input
42741          * @type RegExp
42742          */
42743         'alphaMask' : /[a-z_]/i,
42744
42745         /**
42746          * The function used to validate alphanumeric values
42747          * @param {String} value The value
42748          */
42749         'alphanum' : function(v){
42750             return alphanum.test(v);
42751         },
42752         /**
42753          * The error text to display when the alphanumeric validation function returns false
42754          * @type String
42755          */
42756         'alphanumText' : 'This field should only contain letters, numbers and _',
42757         /**
42758          * The keystroke filter mask to be applied on alphanumeric input
42759          * @type RegExp
42760          */
42761         'alphanumMask' : /[a-z0-9_]/i
42762     };
42763 }();//<script type="text/javascript">
42764
42765 /**
42766  * @class Roo.form.FCKeditor
42767  * @extends Roo.form.TextArea
42768  * Wrapper around the FCKEditor http://www.fckeditor.net
42769  * @constructor
42770  * Creates a new FCKeditor
42771  * @param {Object} config Configuration options
42772  */
42773 Roo.form.FCKeditor = function(config){
42774     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42775     this.addEvents({
42776          /**
42777          * @event editorinit
42778          * Fired when the editor is initialized - you can add extra handlers here..
42779          * @param {FCKeditor} this
42780          * @param {Object} the FCK object.
42781          */
42782         editorinit : true
42783     });
42784     
42785     
42786 };
42787 Roo.form.FCKeditor.editors = { };
42788 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42789 {
42790     //defaultAutoCreate : {
42791     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42792     //},
42793     // private
42794     /**
42795      * @cfg {Object} fck options - see fck manual for details.
42796      */
42797     fckconfig : false,
42798     
42799     /**
42800      * @cfg {Object} fck toolbar set (Basic or Default)
42801      */
42802     toolbarSet : 'Basic',
42803     /**
42804      * @cfg {Object} fck BasePath
42805      */ 
42806     basePath : '/fckeditor/',
42807     
42808     
42809     frame : false,
42810     
42811     value : '',
42812     
42813    
42814     onRender : function(ct, position)
42815     {
42816         if(!this.el){
42817             this.defaultAutoCreate = {
42818                 tag: "textarea",
42819                 style:"width:300px;height:60px;",
42820                 autocomplete: "off"
42821             };
42822         }
42823         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42824         /*
42825         if(this.grow){
42826             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42827             if(this.preventScrollbars){
42828                 this.el.setStyle("overflow", "hidden");
42829             }
42830             this.el.setHeight(this.growMin);
42831         }
42832         */
42833         //console.log('onrender' + this.getId() );
42834         Roo.form.FCKeditor.editors[this.getId()] = this;
42835          
42836
42837         this.replaceTextarea() ;
42838         
42839     },
42840     
42841     getEditor : function() {
42842         return this.fckEditor;
42843     },
42844     /**
42845      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42846      * @param {Mixed} value The value to set
42847      */
42848     
42849     
42850     setValue : function(value)
42851     {
42852         //console.log('setValue: ' + value);
42853         
42854         if(typeof(value) == 'undefined') { // not sure why this is happending...
42855             return;
42856         }
42857         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42858         
42859         //if(!this.el || !this.getEditor()) {
42860         //    this.value = value;
42861             //this.setValue.defer(100,this,[value]);    
42862         //    return;
42863         //} 
42864         
42865         if(!this.getEditor()) {
42866             return;
42867         }
42868         
42869         this.getEditor().SetData(value);
42870         
42871         //
42872
42873     },
42874
42875     /**
42876      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42877      * @return {Mixed} value The field value
42878      */
42879     getValue : function()
42880     {
42881         
42882         if (this.frame && this.frame.dom.style.display == 'none') {
42883             return Roo.form.FCKeditor.superclass.getValue.call(this);
42884         }
42885         
42886         if(!this.el || !this.getEditor()) {
42887            
42888            // this.getValue.defer(100,this); 
42889             return this.value;
42890         }
42891        
42892         
42893         var value=this.getEditor().GetData();
42894         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42895         return Roo.form.FCKeditor.superclass.getValue.call(this);
42896         
42897
42898     },
42899
42900     /**
42901      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42902      * @return {Mixed} value The field value
42903      */
42904     getRawValue : function()
42905     {
42906         if (this.frame && this.frame.dom.style.display == 'none') {
42907             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42908         }
42909         
42910         if(!this.el || !this.getEditor()) {
42911             //this.getRawValue.defer(100,this); 
42912             return this.value;
42913             return;
42914         }
42915         
42916         
42917         
42918         var value=this.getEditor().GetData();
42919         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42920         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42921          
42922     },
42923     
42924     setSize : function(w,h) {
42925         
42926         
42927         
42928         //if (this.frame && this.frame.dom.style.display == 'none') {
42929         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42930         //    return;
42931         //}
42932         //if(!this.el || !this.getEditor()) {
42933         //    this.setSize.defer(100,this, [w,h]); 
42934         //    return;
42935         //}
42936         
42937         
42938         
42939         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42940         
42941         this.frame.dom.setAttribute('width', w);
42942         this.frame.dom.setAttribute('height', h);
42943         this.frame.setSize(w,h);
42944         
42945     },
42946     
42947     toggleSourceEdit : function(value) {
42948         
42949       
42950          
42951         this.el.dom.style.display = value ? '' : 'none';
42952         this.frame.dom.style.display = value ?  'none' : '';
42953         
42954     },
42955     
42956     
42957     focus: function(tag)
42958     {
42959         if (this.frame.dom.style.display == 'none') {
42960             return Roo.form.FCKeditor.superclass.focus.call(this);
42961         }
42962         if(!this.el || !this.getEditor()) {
42963             this.focus.defer(100,this, [tag]); 
42964             return;
42965         }
42966         
42967         
42968         
42969         
42970         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42971         this.getEditor().Focus();
42972         if (tgs.length) {
42973             if (!this.getEditor().Selection.GetSelection()) {
42974                 this.focus.defer(100,this, [tag]); 
42975                 return;
42976             }
42977             
42978             
42979             var r = this.getEditor().EditorDocument.createRange();
42980             r.setStart(tgs[0],0);
42981             r.setEnd(tgs[0],0);
42982             this.getEditor().Selection.GetSelection().removeAllRanges();
42983             this.getEditor().Selection.GetSelection().addRange(r);
42984             this.getEditor().Focus();
42985         }
42986         
42987     },
42988     
42989     
42990     
42991     replaceTextarea : function()
42992     {
42993         if ( document.getElementById( this.getId() + '___Frame' ) )
42994             return ;
42995         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42996         //{
42997             // We must check the elements firstly using the Id and then the name.
42998         var oTextarea = document.getElementById( this.getId() );
42999         
43000         var colElementsByName = document.getElementsByName( this.getId() ) ;
43001          
43002         oTextarea.style.display = 'none' ;
43003
43004         if ( oTextarea.tabIndex ) {            
43005             this.TabIndex = oTextarea.tabIndex ;
43006         }
43007         
43008         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43009         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43010         this.frame = Roo.get(this.getId() + '___Frame')
43011     },
43012     
43013     _getConfigHtml : function()
43014     {
43015         var sConfig = '' ;
43016
43017         for ( var o in this.fckconfig ) {
43018             sConfig += sConfig.length > 0  ? '&amp;' : '';
43019             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43020         }
43021
43022         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43023     },
43024     
43025     
43026     _getIFrameHtml : function()
43027     {
43028         var sFile = 'fckeditor.html' ;
43029         /* no idea what this is about..
43030         try
43031         {
43032             if ( (/fcksource=true/i).test( window.top.location.search ) )
43033                 sFile = 'fckeditor.original.html' ;
43034         }
43035         catch (e) { 
43036         */
43037
43038         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43039         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43040         
43041         
43042         var html = '<iframe id="' + this.getId() +
43043             '___Frame" src="' + sLink +
43044             '" width="' + this.width +
43045             '" height="' + this.height + '"' +
43046             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43047             ' frameborder="0" scrolling="no"></iframe>' ;
43048
43049         return html ;
43050     },
43051     
43052     _insertHtmlBefore : function( html, element )
43053     {
43054         if ( element.insertAdjacentHTML )       {
43055             // IE
43056             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43057         } else { // Gecko
43058             var oRange = document.createRange() ;
43059             oRange.setStartBefore( element ) ;
43060             var oFragment = oRange.createContextualFragment( html );
43061             element.parentNode.insertBefore( oFragment, element ) ;
43062         }
43063     }
43064     
43065     
43066   
43067     
43068     
43069     
43070     
43071
43072 });
43073
43074 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43075
43076 function FCKeditor_OnComplete(editorInstance){
43077     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43078     f.fckEditor = editorInstance;
43079     //console.log("loaded");
43080     f.fireEvent('editorinit', f, editorInstance);
43081
43082   
43083
43084  
43085
43086
43087
43088
43089
43090
43091
43092
43093
43094
43095
43096
43097
43098
43099
43100 //<script type="text/javascript">
43101 /**
43102  * @class Roo.form.GridField
43103  * @extends Roo.form.Field
43104  * Embed a grid (or editable grid into a form)
43105  * STATUS ALPHA
43106  * 
43107  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43108  * it needs 
43109  * xgrid.store = Roo.data.Store
43110  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43111  * xgrid.store.reader = Roo.data.JsonReader 
43112  * 
43113  * 
43114  * @constructor
43115  * Creates a new GridField
43116  * @param {Object} config Configuration options
43117  */
43118 Roo.form.GridField = function(config){
43119     Roo.form.GridField.superclass.constructor.call(this, config);
43120      
43121 };
43122
43123 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43124     /**
43125      * @cfg {Number} width  - used to restrict width of grid..
43126      */
43127     width : 100,
43128     /**
43129      * @cfg {Number} height - used to restrict height of grid..
43130      */
43131     height : 50,
43132      /**
43133      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43134          * 
43135          *}
43136      */
43137     xgrid : false, 
43138     /**
43139      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43140      * {tag: "input", type: "checkbox", autocomplete: "off"})
43141      */
43142    // defaultAutoCreate : { tag: 'div' },
43143     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43144     /**
43145      * @cfg {String} addTitle Text to include for adding a title.
43146      */
43147     addTitle : false,
43148     //
43149     onResize : function(){
43150         Roo.form.Field.superclass.onResize.apply(this, arguments);
43151     },
43152
43153     initEvents : function(){
43154         // Roo.form.Checkbox.superclass.initEvents.call(this);
43155         // has no events...
43156        
43157     },
43158
43159
43160     getResizeEl : function(){
43161         return this.wrap;
43162     },
43163
43164     getPositionEl : function(){
43165         return this.wrap;
43166     },
43167
43168     // private
43169     onRender : function(ct, position){
43170         
43171         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43172         var style = this.style;
43173         delete this.style;
43174         
43175         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43176         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43177         this.viewEl = this.wrap.createChild({ tag: 'div' });
43178         if (style) {
43179             this.viewEl.applyStyles(style);
43180         }
43181         if (this.width) {
43182             this.viewEl.setWidth(this.width);
43183         }
43184         if (this.height) {
43185             this.viewEl.setHeight(this.height);
43186         }
43187         //if(this.inputValue !== undefined){
43188         //this.setValue(this.value);
43189         
43190         
43191         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43192         
43193         
43194         this.grid.render();
43195         this.grid.getDataSource().on('remove', this.refreshValue, this);
43196         this.grid.getDataSource().on('update', this.refreshValue, this);
43197         this.grid.on('afteredit', this.refreshValue, this);
43198  
43199     },
43200      
43201     
43202     /**
43203      * Sets the value of the item. 
43204      * @param {String} either an object  or a string..
43205      */
43206     setValue : function(v){
43207         //this.value = v;
43208         v = v || []; // empty set..
43209         // this does not seem smart - it really only affects memoryproxy grids..
43210         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43211             var ds = this.grid.getDataSource();
43212             // assumes a json reader..
43213             var data = {}
43214             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43215             ds.loadData( data);
43216         }
43217         // clear selection so it does not get stale.
43218         if (this.grid.sm) { 
43219             this.grid.sm.clearSelections();
43220         }
43221         
43222         Roo.form.GridField.superclass.setValue.call(this, v);
43223         this.refreshValue();
43224         // should load data in the grid really....
43225     },
43226     
43227     // private
43228     refreshValue: function() {
43229          var val = [];
43230         this.grid.getDataSource().each(function(r) {
43231             val.push(r.data);
43232         });
43233         this.el.dom.value = Roo.encode(val);
43234     }
43235     
43236      
43237     
43238     
43239 });/*
43240  * Based on:
43241  * Ext JS Library 1.1.1
43242  * Copyright(c) 2006-2007, Ext JS, LLC.
43243  *
43244  * Originally Released Under LGPL - original licence link has changed is not relivant.
43245  *
43246  * Fork - LGPL
43247  * <script type="text/javascript">
43248  */
43249 /**
43250  * @class Roo.form.DisplayField
43251  * @extends Roo.form.Field
43252  * A generic Field to display non-editable data.
43253  * @constructor
43254  * Creates a new Display Field item.
43255  * @param {Object} config Configuration options
43256  */
43257 Roo.form.DisplayField = function(config){
43258     Roo.form.DisplayField.superclass.constructor.call(this, config);
43259     
43260 };
43261
43262 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43263     inputType:      'hidden',
43264     allowBlank:     true,
43265     readOnly:         true,
43266     
43267  
43268     /**
43269      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43270      */
43271     focusClass : undefined,
43272     /**
43273      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43274      */
43275     fieldClass: 'x-form-field',
43276     
43277      /**
43278      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43279      */
43280     valueRenderer: undefined,
43281     
43282     width: 100,
43283     /**
43284      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43285      * {tag: "input", type: "checkbox", autocomplete: "off"})
43286      */
43287      
43288  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43289
43290     onResize : function(){
43291         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43292         
43293     },
43294
43295     initEvents : function(){
43296         // Roo.form.Checkbox.superclass.initEvents.call(this);
43297         // has no events...
43298        
43299     },
43300
43301
43302     getResizeEl : function(){
43303         return this.wrap;
43304     },
43305
43306     getPositionEl : function(){
43307         return this.wrap;
43308     },
43309
43310     // private
43311     onRender : function(ct, position){
43312         
43313         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43314         //if(this.inputValue !== undefined){
43315         this.wrap = this.el.wrap();
43316         
43317         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43318         
43319         if (this.bodyStyle) {
43320             this.viewEl.applyStyles(this.bodyStyle);
43321         }
43322         //this.viewEl.setStyle('padding', '2px');
43323         
43324         this.setValue(this.value);
43325         
43326     },
43327 /*
43328     // private
43329     initValue : Roo.emptyFn,
43330
43331   */
43332
43333         // private
43334     onClick : function(){
43335         
43336     },
43337
43338     /**
43339      * Sets the checked state of the checkbox.
43340      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43341      */
43342     setValue : function(v){
43343         this.value = v;
43344         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43345         // this might be called before we have a dom element..
43346         if (!this.viewEl) {
43347             return;
43348         }
43349         this.viewEl.dom.innerHTML = html;
43350         Roo.form.DisplayField.superclass.setValue.call(this, v);
43351
43352     }
43353 });/*
43354  * 
43355  * Licence- LGPL
43356  * 
43357  */
43358
43359 /**
43360  * @class Roo.form.DayPicker
43361  * @extends Roo.form.Field
43362  * A Day picker show [M] [T] [W] ....
43363  * @constructor
43364  * Creates a new Day Picker
43365  * @param {Object} config Configuration options
43366  */
43367 Roo.form.DayPicker= function(config){
43368     Roo.form.DayPicker.superclass.constructor.call(this, config);
43369      
43370 };
43371
43372 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43373     /**
43374      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43375      */
43376     focusClass : undefined,
43377     /**
43378      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43379      */
43380     fieldClass: "x-form-field",
43381    
43382     /**
43383      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43384      * {tag: "input", type: "checkbox", autocomplete: "off"})
43385      */
43386     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43387     
43388    
43389     actionMode : 'viewEl', 
43390     //
43391     // private
43392  
43393     inputType : 'hidden',
43394     
43395      
43396     inputElement: false, // real input element?
43397     basedOn: false, // ????
43398     
43399     isFormField: true, // not sure where this is needed!!!!
43400
43401     onResize : function(){
43402         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43403         if(!this.boxLabel){
43404             this.el.alignTo(this.wrap, 'c-c');
43405         }
43406     },
43407
43408     initEvents : function(){
43409         Roo.form.Checkbox.superclass.initEvents.call(this);
43410         this.el.on("click", this.onClick,  this);
43411         this.el.on("change", this.onClick,  this);
43412     },
43413
43414
43415     getResizeEl : function(){
43416         return this.wrap;
43417     },
43418
43419     getPositionEl : function(){
43420         return this.wrap;
43421     },
43422
43423     
43424     // private
43425     onRender : function(ct, position){
43426         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43427        
43428         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43429         
43430         var r1 = '<table><tr>';
43431         var r2 = '<tr class="x-form-daypick-icons">';
43432         for (var i=0; i < 7; i++) {
43433             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43434             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43435         }
43436         
43437         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43438         viewEl.select('img').on('click', this.onClick, this);
43439         this.viewEl = viewEl;   
43440         
43441         
43442         // this will not work on Chrome!!!
43443         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43444         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43445         
43446         
43447           
43448
43449     },
43450
43451     // private
43452     initValue : Roo.emptyFn,
43453
43454     /**
43455      * Returns the checked state of the checkbox.
43456      * @return {Boolean} True if checked, else false
43457      */
43458     getValue : function(){
43459         return this.el.dom.value;
43460         
43461     },
43462
43463         // private
43464     onClick : function(e){ 
43465         //this.setChecked(!this.checked);
43466         Roo.get(e.target).toggleClass('x-menu-item-checked');
43467         this.refreshValue();
43468         //if(this.el.dom.checked != this.checked){
43469         //    this.setValue(this.el.dom.checked);
43470        // }
43471     },
43472     
43473     // private
43474     refreshValue : function()
43475     {
43476         var val = '';
43477         this.viewEl.select('img',true).each(function(e,i,n)  {
43478             val += e.is(".x-menu-item-checked") ? String(n) : '';
43479         });
43480         this.setValue(val, true);
43481     },
43482
43483     /**
43484      * Sets the checked state of the checkbox.
43485      * On is always based on a string comparison between inputValue and the param.
43486      * @param {Boolean/String} value - the value to set 
43487      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43488      */
43489     setValue : function(v,suppressEvent){
43490         if (!this.el.dom) {
43491             return;
43492         }
43493         var old = this.el.dom.value ;
43494         this.el.dom.value = v;
43495         if (suppressEvent) {
43496             return ;
43497         }
43498          
43499         // update display..
43500         this.viewEl.select('img',true).each(function(e,i,n)  {
43501             
43502             var on = e.is(".x-menu-item-checked");
43503             var newv = v.indexOf(String(n)) > -1;
43504             if (on != newv) {
43505                 e.toggleClass('x-menu-item-checked');
43506             }
43507             
43508         });
43509         
43510         
43511         this.fireEvent('change', this, v, old);
43512         
43513         
43514     },
43515    
43516     // handle setting of hidden value by some other method!!?!?
43517     setFromHidden: function()
43518     {
43519         if(!this.el){
43520             return;
43521         }
43522         //console.log("SET FROM HIDDEN");
43523         //alert('setFrom hidden');
43524         this.setValue(this.el.dom.value);
43525     },
43526     
43527     onDestroy : function()
43528     {
43529         if(this.viewEl){
43530             Roo.get(this.viewEl).remove();
43531         }
43532          
43533         Roo.form.DayPicker.superclass.onDestroy.call(this);
43534     }
43535
43536 });/*
43537  * RooJS Library 1.1.1
43538  * Copyright(c) 2008-2011  Alan Knowles
43539  *
43540  * License - LGPL
43541  */
43542  
43543
43544 /**
43545  * @class Roo.form.ComboCheck
43546  * @extends Roo.form.ComboBox
43547  * A combobox for multiple select items.
43548  *
43549  * FIXME - could do with a reset button..
43550  * 
43551  * @constructor
43552  * Create a new ComboCheck
43553  * @param {Object} config Configuration options
43554  */
43555 Roo.form.ComboCheck = function(config){
43556     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43557     // should verify some data...
43558     // like
43559     // hiddenName = required..
43560     // displayField = required
43561     // valudField == required
43562     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43563     var _t = this;
43564     Roo.each(req, function(e) {
43565         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43566             throw "Roo.form.ComboCheck : missing value for: " + e;
43567         }
43568     });
43569     
43570     
43571 };
43572
43573 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43574      
43575      
43576     editable : false,
43577      
43578     selectedClass: 'x-menu-item-checked', 
43579     
43580     // private
43581     onRender : function(ct, position){
43582         var _t = this;
43583         
43584         
43585         
43586         if(!this.tpl){
43587             var cls = 'x-combo-list';
43588
43589             
43590             this.tpl =  new Roo.Template({
43591                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43592                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43593                    '<span>{' + this.displayField + '}</span>' +
43594                     '</div>' 
43595                 
43596             });
43597         }
43598  
43599         
43600         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43601         this.view.singleSelect = false;
43602         this.view.multiSelect = true;
43603         this.view.toggleSelect = true;
43604         this.pageTb.add(new Roo.Toolbar.Fill(), {
43605             
43606             text: 'Done',
43607             handler: function()
43608             {
43609                 _t.collapse();
43610             }
43611         });
43612     },
43613     
43614     onViewOver : function(e, t){
43615         // do nothing...
43616         return;
43617         
43618     },
43619     
43620     onViewClick : function(doFocus,index){
43621         return;
43622         
43623     },
43624     select: function () {
43625         //Roo.log("SELECT CALLED");
43626     },
43627      
43628     selectByValue : function(xv, scrollIntoView){
43629         var ar = this.getValueArray();
43630         var sels = [];
43631         
43632         Roo.each(ar, function(v) {
43633             if(v === undefined || v === null){
43634                 return;
43635             }
43636             var r = this.findRecord(this.valueField, v);
43637             if(r){
43638                 sels.push(this.store.indexOf(r))
43639                 
43640             }
43641         },this);
43642         this.view.select(sels);
43643         return false;
43644     },
43645     
43646     
43647     
43648     onSelect : function(record, index){
43649        // Roo.log("onselect Called");
43650        // this is only called by the clear button now..
43651         this.view.clearSelections();
43652         this.setValue('[]');
43653         if (this.value != this.valueBefore) {
43654             this.fireEvent('change', this, this.value, this.valueBefore);
43655         }
43656     },
43657     getValueArray : function()
43658     {
43659         var ar = [] ;
43660         
43661         try {
43662             Roo.log(this.value);
43663             var ar = Roo.decode(this.value);
43664             return  ar instanceof Array ? ar : []; //?? valid?
43665             
43666         } catch(e) {
43667             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43668             return [];
43669         }
43670          
43671     },
43672     expand : function ()
43673     {
43674         Roo.form.ComboCheck.superclass.expand.call(this);
43675         this.valueBefore = this.value;
43676         
43677
43678     },
43679     
43680     collapse : function(){
43681         Roo.form.ComboCheck.superclass.collapse.call(this);
43682         var sl = this.view.getSelectedIndexes();
43683         var st = this.store;
43684         var nv = [];
43685         var tv = [];
43686         var r;
43687         Roo.each(sl, function(i) {
43688             r = st.getAt(i);
43689             nv.push(r.get(this.valueField));
43690         },this);
43691         this.setValue(Roo.encode(nv));
43692         if (this.value != this.valueBefore) {
43693
43694             this.fireEvent('change', this, this.value, this.valueBefore);
43695         }
43696         
43697     },
43698     
43699     setValue : function(v){
43700         // Roo.log(v);
43701         this.value = v;
43702         
43703         var vals = this.getValueArray();
43704         var tv = [];
43705         Roo.each(vals, function(k) {
43706             var r = this.findRecord(this.valueField, k);
43707             if(r){
43708                 tv.push(r.data[this.displayField]);
43709             }else if(this.valueNotFoundText !== undefined){
43710                 tv.push( this.valueNotFoundText );
43711             }
43712         },this);
43713        // Roo.log(tv);
43714         
43715         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43716         this.hiddenField.value = v;
43717         this.value = v;
43718     }
43719     
43720 });//<script type="text/javasscript">
43721  
43722
43723 /**
43724  * @class Roo.DDView
43725  * A DnD enabled version of Roo.View.
43726  * @param {Element/String} container The Element in which to create the View.
43727  * @param {String} tpl The template string used to create the markup for each element of the View
43728  * @param {Object} config The configuration properties. These include all the config options of
43729  * {@link Roo.View} plus some specific to this class.<br>
43730  * <p>
43731  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43732  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43733  * <p>
43734  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43735 .x-view-drag-insert-above {
43736         border-top:1px dotted #3366cc;
43737 }
43738 .x-view-drag-insert-below {
43739         border-bottom:1px dotted #3366cc;
43740 }
43741 </code></pre>
43742  * 
43743  */
43744  
43745 Roo.DDView = function(container, tpl, config) {
43746     Roo.DDView.superclass.constructor.apply(this, arguments);
43747     this.getEl().setStyle("outline", "0px none");
43748     this.getEl().unselectable();
43749     if (this.dragGroup) {
43750                 this.setDraggable(this.dragGroup.split(","));
43751     }
43752     if (this.dropGroup) {
43753                 this.setDroppable(this.dropGroup.split(","));
43754     }
43755     if (this.deletable) {
43756         this.setDeletable();
43757     }
43758     this.isDirtyFlag = false;
43759         this.addEvents({
43760                 "drop" : true
43761         });
43762 };
43763
43764 Roo.extend(Roo.DDView, Roo.View, {
43765 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43766 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43767 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43768 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43769
43770         isFormField: true,
43771
43772         reset: Roo.emptyFn,
43773         
43774         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43775
43776         validate: function() {
43777                 return true;
43778         },
43779         
43780         destroy: function() {
43781                 this.purgeListeners();
43782                 this.getEl.removeAllListeners();
43783                 this.getEl().remove();
43784                 if (this.dragZone) {
43785                         if (this.dragZone.destroy) {
43786                                 this.dragZone.destroy();
43787                         }
43788                 }
43789                 if (this.dropZone) {
43790                         if (this.dropZone.destroy) {
43791                                 this.dropZone.destroy();
43792                         }
43793                 }
43794         },
43795
43796 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43797         getName: function() {
43798                 return this.name;
43799         },
43800
43801 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43802         setValue: function(v) {
43803                 if (!this.store) {
43804                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43805                 }
43806                 var data = {};
43807                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43808                 this.store.proxy = new Roo.data.MemoryProxy(data);
43809                 this.store.load();
43810         },
43811
43812 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43813         getValue: function() {
43814                 var result = '(';
43815                 this.store.each(function(rec) {
43816                         result += rec.id + ',';
43817                 });
43818                 return result.substr(0, result.length - 1) + ')';
43819         },
43820         
43821         getIds: function() {
43822                 var i = 0, result = new Array(this.store.getCount());
43823                 this.store.each(function(rec) {
43824                         result[i++] = rec.id;
43825                 });
43826                 return result;
43827         },
43828         
43829         isDirty: function() {
43830                 return this.isDirtyFlag;
43831         },
43832
43833 /**
43834  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43835  *      whole Element becomes the target, and this causes the drop gesture to append.
43836  */
43837     getTargetFromEvent : function(e) {
43838                 var target = e.getTarget();
43839                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43840                 target = target.parentNode;
43841                 }
43842                 if (!target) {
43843                         target = this.el.dom.lastChild || this.el.dom;
43844                 }
43845                 return target;
43846     },
43847
43848 /**
43849  *      Create the drag data which consists of an object which has the property "ddel" as
43850  *      the drag proxy element. 
43851  */
43852     getDragData : function(e) {
43853         var target = this.findItemFromChild(e.getTarget());
43854                 if(target) {
43855                         this.handleSelection(e);
43856                         var selNodes = this.getSelectedNodes();
43857             var dragData = {
43858                 source: this,
43859                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43860                 nodes: selNodes,
43861                 records: []
43862                         };
43863                         var selectedIndices = this.getSelectedIndexes();
43864                         for (var i = 0; i < selectedIndices.length; i++) {
43865                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43866                         }
43867                         if (selNodes.length == 1) {
43868                                 dragData.ddel = target.cloneNode(true); // the div element
43869                         } else {
43870                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43871                                 div.className = 'multi-proxy';
43872                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43873                                         div.appendChild(selNodes[i].cloneNode(true));
43874                                 }
43875                                 dragData.ddel = div;
43876                         }
43877             //console.log(dragData)
43878             //console.log(dragData.ddel.innerHTML)
43879                         return dragData;
43880                 }
43881         //console.log('nodragData')
43882                 return false;
43883     },
43884     
43885 /**     Specify to which ddGroup items in this DDView may be dragged. */
43886     setDraggable: function(ddGroup) {
43887         if (ddGroup instanceof Array) {
43888                 Roo.each(ddGroup, this.setDraggable, this);
43889                 return;
43890         }
43891         if (this.dragZone) {
43892                 this.dragZone.addToGroup(ddGroup);
43893         } else {
43894                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43895                                 containerScroll: true,
43896                                 ddGroup: ddGroup 
43897
43898                         });
43899 //                      Draggability implies selection. DragZone's mousedown selects the element.
43900                         if (!this.multiSelect) { this.singleSelect = true; }
43901
43902 //                      Wire the DragZone's handlers up to methods in *this*
43903                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43904                 }
43905     },
43906
43907 /**     Specify from which ddGroup this DDView accepts drops. */
43908     setDroppable: function(ddGroup) {
43909         if (ddGroup instanceof Array) {
43910                 Roo.each(ddGroup, this.setDroppable, this);
43911                 return;
43912         }
43913         if (this.dropZone) {
43914                 this.dropZone.addToGroup(ddGroup);
43915         } else {
43916                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43917                                 containerScroll: true,
43918                                 ddGroup: ddGroup
43919                         });
43920
43921 //                      Wire the DropZone's handlers up to methods in *this*
43922                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43923                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43924                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43925                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43926                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43927                 }
43928     },
43929
43930 /**     Decide whether to drop above or below a View node. */
43931     getDropPoint : function(e, n, dd){
43932         if (n == this.el.dom) { return "above"; }
43933                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43934                 var c = t + (b - t) / 2;
43935                 var y = Roo.lib.Event.getPageY(e);
43936                 if(y <= c) {
43937                         return "above";
43938                 }else{
43939                         return "below";
43940                 }
43941     },
43942
43943     onNodeEnter : function(n, dd, e, data){
43944                 return false;
43945     },
43946     
43947     onNodeOver : function(n, dd, e, data){
43948                 var pt = this.getDropPoint(e, n, dd);
43949                 // set the insert point style on the target node
43950                 var dragElClass = this.dropNotAllowed;
43951                 if (pt) {
43952                         var targetElClass;
43953                         if (pt == "above"){
43954                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43955                                 targetElClass = "x-view-drag-insert-above";
43956                         } else {
43957                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43958                                 targetElClass = "x-view-drag-insert-below";
43959                         }
43960                         if (this.lastInsertClass != targetElClass){
43961                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43962                                 this.lastInsertClass = targetElClass;
43963                         }
43964                 }
43965                 return dragElClass;
43966         },
43967
43968     onNodeOut : function(n, dd, e, data){
43969                 this.removeDropIndicators(n);
43970     },
43971
43972     onNodeDrop : function(n, dd, e, data){
43973         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43974                 return false;
43975         }
43976         var pt = this.getDropPoint(e, n, dd);
43977                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43978                 if (pt == "below") { insertAt++; }
43979                 for (var i = 0; i < data.records.length; i++) {
43980                         var r = data.records[i];
43981                         var dup = this.store.getById(r.id);
43982                         if (dup && (dd != this.dragZone)) {
43983                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43984                         } else {
43985                                 if (data.copy) {
43986                                         this.store.insert(insertAt++, r.copy());
43987                                 } else {
43988                                         data.source.isDirtyFlag = true;
43989                                         r.store.remove(r);
43990                                         this.store.insert(insertAt++, r);
43991                                 }
43992                                 this.isDirtyFlag = true;
43993                         }
43994                 }
43995                 this.dragZone.cachedTarget = null;
43996                 return true;
43997     },
43998
43999     removeDropIndicators : function(n){
44000                 if(n){
44001                         Roo.fly(n).removeClass([
44002                                 "x-view-drag-insert-above",
44003                                 "x-view-drag-insert-below"]);
44004                         this.lastInsertClass = "_noclass";
44005                 }
44006     },
44007
44008 /**
44009  *      Utility method. Add a delete option to the DDView's context menu.
44010  *      @param {String} imageUrl The URL of the "delete" icon image.
44011  */
44012         setDeletable: function(imageUrl) {
44013                 if (!this.singleSelect && !this.multiSelect) {
44014                         this.singleSelect = true;
44015                 }
44016                 var c = this.getContextMenu();
44017                 this.contextMenu.on("itemclick", function(item) {
44018                         switch (item.id) {
44019                                 case "delete":
44020                                         this.remove(this.getSelectedIndexes());
44021                                         break;
44022                         }
44023                 }, this);
44024                 this.contextMenu.add({
44025                         icon: imageUrl,
44026                         id: "delete",
44027                         text: 'Delete'
44028                 });
44029         },
44030         
44031 /**     Return the context menu for this DDView. */
44032         getContextMenu: function() {
44033                 if (!this.contextMenu) {
44034 //                      Create the View's context menu
44035                         this.contextMenu = new Roo.menu.Menu({
44036                                 id: this.id + "-contextmenu"
44037                         });
44038                         this.el.on("contextmenu", this.showContextMenu, this);
44039                 }
44040                 return this.contextMenu;
44041         },
44042         
44043         disableContextMenu: function() {
44044                 if (this.contextMenu) {
44045                         this.el.un("contextmenu", this.showContextMenu, this);
44046                 }
44047         },
44048
44049         showContextMenu: function(e, item) {
44050         item = this.findItemFromChild(e.getTarget());
44051                 if (item) {
44052                         e.stopEvent();
44053                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44054                         this.contextMenu.showAt(e.getXY());
44055             }
44056     },
44057
44058 /**
44059  *      Remove {@link Roo.data.Record}s at the specified indices.
44060  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44061  */
44062     remove: function(selectedIndices) {
44063                 selectedIndices = [].concat(selectedIndices);
44064                 for (var i = 0; i < selectedIndices.length; i++) {
44065                         var rec = this.store.getAt(selectedIndices[i]);
44066                         this.store.remove(rec);
44067                 }
44068     },
44069
44070 /**
44071  *      Double click fires the event, but also, if this is draggable, and there is only one other
44072  *      related DropZone, it transfers the selected node.
44073  */
44074     onDblClick : function(e){
44075         var item = this.findItemFromChild(e.getTarget());
44076         if(item){
44077             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44078                 return false;
44079             }
44080             if (this.dragGroup) {
44081                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44082                     while (targets.indexOf(this.dropZone) > -1) {
44083                             targets.remove(this.dropZone);
44084                                 }
44085                     if (targets.length == 1) {
44086                                         this.dragZone.cachedTarget = null;
44087                         var el = Roo.get(targets[0].getEl());
44088                         var box = el.getBox(true);
44089                         targets[0].onNodeDrop(el.dom, {
44090                                 target: el.dom,
44091                                 xy: [box.x, box.y + box.height - 1]
44092                         }, null, this.getDragData(e));
44093                     }
44094                 }
44095         }
44096     },
44097     
44098     handleSelection: function(e) {
44099                 this.dragZone.cachedTarget = null;
44100         var item = this.findItemFromChild(e.getTarget());
44101         if (!item) {
44102                 this.clearSelections(true);
44103                 return;
44104         }
44105                 if (item && (this.multiSelect || this.singleSelect)){
44106                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44107                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44108                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44109                                 this.unselect(item);
44110                         } else {
44111                                 this.select(item, this.multiSelect && e.ctrlKey);
44112                                 this.lastSelection = item;
44113                         }
44114                 }
44115     },
44116
44117     onItemClick : function(item, index, e){
44118                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44119                         return false;
44120                 }
44121                 return true;
44122     },
44123
44124     unselect : function(nodeInfo, suppressEvent){
44125                 var node = this.getNode(nodeInfo);
44126                 if(node && this.isSelected(node)){
44127                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44128                                 Roo.fly(node).removeClass(this.selectedClass);
44129                                 this.selections.remove(node);
44130                                 if(!suppressEvent){
44131                                         this.fireEvent("selectionchange", this, this.selections);
44132                                 }
44133                         }
44134                 }
44135     }
44136 });
44137 /*
44138  * Based on:
44139  * Ext JS Library 1.1.1
44140  * Copyright(c) 2006-2007, Ext JS, LLC.
44141  *
44142  * Originally Released Under LGPL - original licence link has changed is not relivant.
44143  *
44144  * Fork - LGPL
44145  * <script type="text/javascript">
44146  */
44147  
44148 /**
44149  * @class Roo.LayoutManager
44150  * @extends Roo.util.Observable
44151  * Base class for layout managers.
44152  */
44153 Roo.LayoutManager = function(container, config){
44154     Roo.LayoutManager.superclass.constructor.call(this);
44155     this.el = Roo.get(container);
44156     // ie scrollbar fix
44157     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44158         document.body.scroll = "no";
44159     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44160         this.el.position('relative');
44161     }
44162     this.id = this.el.id;
44163     this.el.addClass("x-layout-container");
44164     /** false to disable window resize monitoring @type Boolean */
44165     this.monitorWindowResize = true;
44166     this.regions = {};
44167     this.addEvents({
44168         /**
44169          * @event layout
44170          * Fires when a layout is performed. 
44171          * @param {Roo.LayoutManager} this
44172          */
44173         "layout" : true,
44174         /**
44175          * @event regionresized
44176          * Fires when the user resizes a region. 
44177          * @param {Roo.LayoutRegion} region The resized region
44178          * @param {Number} newSize The new size (width for east/west, height for north/south)
44179          */
44180         "regionresized" : true,
44181         /**
44182          * @event regioncollapsed
44183          * Fires when a region is collapsed. 
44184          * @param {Roo.LayoutRegion} region The collapsed region
44185          */
44186         "regioncollapsed" : true,
44187         /**
44188          * @event regionexpanded
44189          * Fires when a region is expanded.  
44190          * @param {Roo.LayoutRegion} region The expanded region
44191          */
44192         "regionexpanded" : true
44193     });
44194     this.updating = false;
44195     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44196 };
44197
44198 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44199     /**
44200      * Returns true if this layout is currently being updated
44201      * @return {Boolean}
44202      */
44203     isUpdating : function(){
44204         return this.updating; 
44205     },
44206     
44207     /**
44208      * Suspend the LayoutManager from doing auto-layouts while
44209      * making multiple add or remove calls
44210      */
44211     beginUpdate : function(){
44212         this.updating = true;    
44213     },
44214     
44215     /**
44216      * Restore auto-layouts and optionally disable the manager from performing a layout
44217      * @param {Boolean} noLayout true to disable a layout update 
44218      */
44219     endUpdate : function(noLayout){
44220         this.updating = false;
44221         if(!noLayout){
44222             this.layout();
44223         }    
44224     },
44225     
44226     layout: function(){
44227         
44228     },
44229     
44230     onRegionResized : function(region, newSize){
44231         this.fireEvent("regionresized", region, newSize);
44232         this.layout();
44233     },
44234     
44235     onRegionCollapsed : function(region){
44236         this.fireEvent("regioncollapsed", region);
44237     },
44238     
44239     onRegionExpanded : function(region){
44240         this.fireEvent("regionexpanded", region);
44241     },
44242         
44243     /**
44244      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44245      * performs box-model adjustments.
44246      * @return {Object} The size as an object {width: (the width), height: (the height)}
44247      */
44248     getViewSize : function(){
44249         var size;
44250         if(this.el.dom != document.body){
44251             size = this.el.getSize();
44252         }else{
44253             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44254         }
44255         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44256         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44257         return size;
44258     },
44259     
44260     /**
44261      * Returns the Element this layout is bound to.
44262      * @return {Roo.Element}
44263      */
44264     getEl : function(){
44265         return this.el;
44266     },
44267     
44268     /**
44269      * Returns the specified region.
44270      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44271      * @return {Roo.LayoutRegion}
44272      */
44273     getRegion : function(target){
44274         return this.regions[target.toLowerCase()];
44275     },
44276     
44277     onWindowResize : function(){
44278         if(this.monitorWindowResize){
44279             this.layout();
44280         }
44281     }
44282 });/*
44283  * Based on:
44284  * Ext JS Library 1.1.1
44285  * Copyright(c) 2006-2007, Ext JS, LLC.
44286  *
44287  * Originally Released Under LGPL - original licence link has changed is not relivant.
44288  *
44289  * Fork - LGPL
44290  * <script type="text/javascript">
44291  */
44292 /**
44293  * @class Roo.BorderLayout
44294  * @extends Roo.LayoutManager
44295  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44296  * please see: <br><br>
44297  * <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>
44298  * <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>
44299  * Example:
44300  <pre><code>
44301  var layout = new Roo.BorderLayout(document.body, {
44302     north: {
44303         initialSize: 25,
44304         titlebar: false
44305     },
44306     west: {
44307         split:true,
44308         initialSize: 200,
44309         minSize: 175,
44310         maxSize: 400,
44311         titlebar: true,
44312         collapsible: true
44313     },
44314     east: {
44315         split:true,
44316         initialSize: 202,
44317         minSize: 175,
44318         maxSize: 400,
44319         titlebar: true,
44320         collapsible: true
44321     },
44322     south: {
44323         split:true,
44324         initialSize: 100,
44325         minSize: 100,
44326         maxSize: 200,
44327         titlebar: true,
44328         collapsible: true
44329     },
44330     center: {
44331         titlebar: true,
44332         autoScroll:true,
44333         resizeTabs: true,
44334         minTabWidth: 50,
44335         preferredTabWidth: 150
44336     }
44337 });
44338
44339 // shorthand
44340 var CP = Roo.ContentPanel;
44341
44342 layout.beginUpdate();
44343 layout.add("north", new CP("north", "North"));
44344 layout.add("south", new CP("south", {title: "South", closable: true}));
44345 layout.add("west", new CP("west", {title: "West"}));
44346 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44347 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44348 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44349 layout.getRegion("center").showPanel("center1");
44350 layout.endUpdate();
44351 </code></pre>
44352
44353 <b>The container the layout is rendered into can be either the body element or any other element.
44354 If it is not the body element, the container needs to either be an absolute positioned element,
44355 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44356 the container size if it is not the body element.</b>
44357
44358 * @constructor
44359 * Create a new BorderLayout
44360 * @param {String/HTMLElement/Element} container The container this layout is bound to
44361 * @param {Object} config Configuration options
44362  */
44363 Roo.BorderLayout = function(container, config){
44364     config = config || {};
44365     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44366     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44367     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44368         var target = this.factory.validRegions[i];
44369         if(config[target]){
44370             this.addRegion(target, config[target]);
44371         }
44372     }
44373 };
44374
44375 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44376     /**
44377      * Creates and adds a new region if it doesn't already exist.
44378      * @param {String} target The target region key (north, south, east, west or center).
44379      * @param {Object} config The regions config object
44380      * @return {BorderLayoutRegion} The new region
44381      */
44382     addRegion : function(target, config){
44383         if(!this.regions[target]){
44384             var r = this.factory.create(target, this, config);
44385             this.bindRegion(target, r);
44386         }
44387         return this.regions[target];
44388     },
44389
44390     // private (kinda)
44391     bindRegion : function(name, r){
44392         this.regions[name] = r;
44393         r.on("visibilitychange", this.layout, this);
44394         r.on("paneladded", this.layout, this);
44395         r.on("panelremoved", this.layout, this);
44396         r.on("invalidated", this.layout, this);
44397         r.on("resized", this.onRegionResized, this);
44398         r.on("collapsed", this.onRegionCollapsed, this);
44399         r.on("expanded", this.onRegionExpanded, this);
44400     },
44401
44402     /**
44403      * Performs a layout update.
44404      */
44405     layout : function(){
44406         if(this.updating) return;
44407         var size = this.getViewSize();
44408         var w = size.width;
44409         var h = size.height;
44410         var centerW = w;
44411         var centerH = h;
44412         var centerY = 0;
44413         var centerX = 0;
44414         //var x = 0, y = 0;
44415
44416         var rs = this.regions;
44417         var north = rs["north"];
44418         var south = rs["south"]; 
44419         var west = rs["west"];
44420         var east = rs["east"];
44421         var center = rs["center"];
44422         //if(this.hideOnLayout){ // not supported anymore
44423             //c.el.setStyle("display", "none");
44424         //}
44425         if(north && north.isVisible()){
44426             var b = north.getBox();
44427             var m = north.getMargins();
44428             b.width = w - (m.left+m.right);
44429             b.x = m.left;
44430             b.y = m.top;
44431             centerY = b.height + b.y + m.bottom;
44432             centerH -= centerY;
44433             north.updateBox(this.safeBox(b));
44434         }
44435         if(south && south.isVisible()){
44436             var b = south.getBox();
44437             var m = south.getMargins();
44438             b.width = w - (m.left+m.right);
44439             b.x = m.left;
44440             var totalHeight = (b.height + m.top + m.bottom);
44441             b.y = h - totalHeight + m.top;
44442             centerH -= totalHeight;
44443             south.updateBox(this.safeBox(b));
44444         }
44445         if(west && west.isVisible()){
44446             var b = west.getBox();
44447             var m = west.getMargins();
44448             b.height = centerH - (m.top+m.bottom);
44449             b.x = m.left;
44450             b.y = centerY + m.top;
44451             var totalWidth = (b.width + m.left + m.right);
44452             centerX += totalWidth;
44453             centerW -= totalWidth;
44454             west.updateBox(this.safeBox(b));
44455         }
44456         if(east && east.isVisible()){
44457             var b = east.getBox();
44458             var m = east.getMargins();
44459             b.height = centerH - (m.top+m.bottom);
44460             var totalWidth = (b.width + m.left + m.right);
44461             b.x = w - totalWidth + m.left;
44462             b.y = centerY + m.top;
44463             centerW -= totalWidth;
44464             east.updateBox(this.safeBox(b));
44465         }
44466         if(center){
44467             var m = center.getMargins();
44468             var centerBox = {
44469                 x: centerX + m.left,
44470                 y: centerY + m.top,
44471                 width: centerW - (m.left+m.right),
44472                 height: centerH - (m.top+m.bottom)
44473             };
44474             //if(this.hideOnLayout){
44475                 //center.el.setStyle("display", "block");
44476             //}
44477             center.updateBox(this.safeBox(centerBox));
44478         }
44479         this.el.repaint();
44480         this.fireEvent("layout", this);
44481     },
44482
44483     // private
44484     safeBox : function(box){
44485         box.width = Math.max(0, box.width);
44486         box.height = Math.max(0, box.height);
44487         return box;
44488     },
44489
44490     /**
44491      * Adds a ContentPanel (or subclass) to this layout.
44492      * @param {String} target The target region key (north, south, east, west or center).
44493      * @param {Roo.ContentPanel} panel The panel to add
44494      * @return {Roo.ContentPanel} The added panel
44495      */
44496     add : function(target, panel){
44497          
44498         target = target.toLowerCase();
44499         return this.regions[target].add(panel);
44500     },
44501
44502     /**
44503      * Remove a ContentPanel (or subclass) to this layout.
44504      * @param {String} target The target region key (north, south, east, west or center).
44505      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44506      * @return {Roo.ContentPanel} The removed panel
44507      */
44508     remove : function(target, panel){
44509         target = target.toLowerCase();
44510         return this.regions[target].remove(panel);
44511     },
44512
44513     /**
44514      * Searches all regions for a panel with the specified id
44515      * @param {String} panelId
44516      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44517      */
44518     findPanel : function(panelId){
44519         var rs = this.regions;
44520         for(var target in rs){
44521             if(typeof rs[target] != "function"){
44522                 var p = rs[target].getPanel(panelId);
44523                 if(p){
44524                     return p;
44525                 }
44526             }
44527         }
44528         return null;
44529     },
44530
44531     /**
44532      * Searches all regions for a panel with the specified id and activates (shows) it.
44533      * @param {String/ContentPanel} panelId The panels id or the panel itself
44534      * @return {Roo.ContentPanel} The shown panel or null
44535      */
44536     showPanel : function(panelId) {
44537       var rs = this.regions;
44538       for(var target in rs){
44539          var r = rs[target];
44540          if(typeof r != "function"){
44541             if(r.hasPanel(panelId)){
44542                return r.showPanel(panelId);
44543             }
44544          }
44545       }
44546       return null;
44547    },
44548
44549    /**
44550      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44551      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44552      */
44553     restoreState : function(provider){
44554         if(!provider){
44555             provider = Roo.state.Manager;
44556         }
44557         var sm = new Roo.LayoutStateManager();
44558         sm.init(this, provider);
44559     },
44560
44561     /**
44562      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44563      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44564      * a valid ContentPanel config object.  Example:
44565      * <pre><code>
44566 // Create the main layout
44567 var layout = new Roo.BorderLayout('main-ct', {
44568     west: {
44569         split:true,
44570         minSize: 175,
44571         titlebar: true
44572     },
44573     center: {
44574         title:'Components'
44575     }
44576 }, 'main-ct');
44577
44578 // Create and add multiple ContentPanels at once via configs
44579 layout.batchAdd({
44580    west: {
44581        id: 'source-files',
44582        autoCreate:true,
44583        title:'Ext Source Files',
44584        autoScroll:true,
44585        fitToFrame:true
44586    },
44587    center : {
44588        el: cview,
44589        autoScroll:true,
44590        fitToFrame:true,
44591        toolbar: tb,
44592        resizeEl:'cbody'
44593    }
44594 });
44595 </code></pre>
44596      * @param {Object} regions An object containing ContentPanel configs by region name
44597      */
44598     batchAdd : function(regions){
44599         this.beginUpdate();
44600         for(var rname in regions){
44601             var lr = this.regions[rname];
44602             if(lr){
44603                 this.addTypedPanels(lr, regions[rname]);
44604             }
44605         }
44606         this.endUpdate();
44607     },
44608
44609     // private
44610     addTypedPanels : function(lr, ps){
44611         if(typeof ps == 'string'){
44612             lr.add(new Roo.ContentPanel(ps));
44613         }
44614         else if(ps instanceof Array){
44615             for(var i =0, len = ps.length; i < len; i++){
44616                 this.addTypedPanels(lr, ps[i]);
44617             }
44618         }
44619         else if(!ps.events){ // raw config?
44620             var el = ps.el;
44621             delete ps.el; // prevent conflict
44622             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44623         }
44624         else {  // panel object assumed!
44625             lr.add(ps);
44626         }
44627     },
44628     /**
44629      * Adds a xtype elements to the layout.
44630      * <pre><code>
44631
44632 layout.addxtype({
44633        xtype : 'ContentPanel',
44634        region: 'west',
44635        items: [ .... ]
44636    }
44637 );
44638
44639 layout.addxtype({
44640         xtype : 'NestedLayoutPanel',
44641         region: 'west',
44642         layout: {
44643            center: { },
44644            west: { }   
44645         },
44646         items : [ ... list of content panels or nested layout panels.. ]
44647    }
44648 );
44649 </code></pre>
44650      * @param {Object} cfg Xtype definition of item to add.
44651      */
44652     addxtype : function(cfg)
44653     {
44654         // basically accepts a pannel...
44655         // can accept a layout region..!?!?
44656         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44657         
44658         if (!cfg.xtype.match(/Panel$/)) {
44659             return false;
44660         }
44661         var ret = false;
44662         
44663         if (typeof(cfg.region) == 'undefined') {
44664             Roo.log("Failed to add Panel, region was not set");
44665             Roo.log(cfg);
44666             return false;
44667         }
44668         var region = cfg.region;
44669         delete cfg.region;
44670         
44671           
44672         var xitems = [];
44673         if (cfg.items) {
44674             xitems = cfg.items;
44675             delete cfg.items;
44676         }
44677         
44678         
44679         switch(cfg.xtype) 
44680         {
44681             case 'ContentPanel':  // ContentPanel (el, cfg)
44682             case 'ScrollPanel':  // ContentPanel (el, cfg)
44683                 if(cfg.autoCreate) {
44684                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44685                 } else {
44686                     var el = this.el.createChild();
44687                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44688                 }
44689                 
44690                 this.add(region, ret);
44691                 break;
44692             
44693             
44694             case 'TreePanel': // our new panel!
44695                 cfg.el = this.el.createChild();
44696                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44697                 this.add(region, ret);
44698                 break;
44699             
44700             case 'NestedLayoutPanel': 
44701                 // create a new Layout (which is  a Border Layout...
44702                 var el = this.el.createChild();
44703                 var clayout = cfg.layout;
44704                 delete cfg.layout;
44705                 clayout.items   = clayout.items  || [];
44706                 // replace this exitems with the clayout ones..
44707                 xitems = clayout.items;
44708                  
44709                 
44710                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44711                     cfg.background = false;
44712                 }
44713                 var layout = new Roo.BorderLayout(el, clayout);
44714                 
44715                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44716                 //console.log('adding nested layout panel '  + cfg.toSource());
44717                 this.add(region, ret);
44718                 
44719                 break;
44720                 
44721             case 'GridPanel': 
44722             
44723                 // needs grid and region
44724                 
44725                 //var el = this.getRegion(region).el.createChild();
44726                 var el = this.el.createChild();
44727                 // create the grid first...
44728                 
44729                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44730                 delete cfg.grid;
44731                 if (region == 'center' && this.active ) {
44732                     cfg.background = false;
44733                 }
44734                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44735                 
44736                 this.add(region, ret);
44737                 if (cfg.background) {
44738                     ret.on('activate', function(gp) {
44739                         if (!gp.grid.rendered) {
44740                             gp.grid.render();
44741                         }
44742                     });
44743                 } else {
44744                     grid.render();
44745                 }
44746                 break;
44747            
44748                
44749                 
44750                 
44751             default: 
44752                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44753                 return null;
44754              // GridPanel (grid, cfg)
44755             
44756         }
44757         this.beginUpdate();
44758         // add children..
44759         Roo.each(xitems, function(i)  {
44760             ret.addxtype(i);
44761         });
44762         this.endUpdate();
44763         return ret;
44764         
44765     }
44766 });
44767
44768 /**
44769  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44770  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44771  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44772  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44773  * <pre><code>
44774 // shorthand
44775 var CP = Roo.ContentPanel;
44776
44777 var layout = Roo.BorderLayout.create({
44778     north: {
44779         initialSize: 25,
44780         titlebar: false,
44781         panels: [new CP("north", "North")]
44782     },
44783     west: {
44784         split:true,
44785         initialSize: 200,
44786         minSize: 175,
44787         maxSize: 400,
44788         titlebar: true,
44789         collapsible: true,
44790         panels: [new CP("west", {title: "West"})]
44791     },
44792     east: {
44793         split:true,
44794         initialSize: 202,
44795         minSize: 175,
44796         maxSize: 400,
44797         titlebar: true,
44798         collapsible: true,
44799         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44800     },
44801     south: {
44802         split:true,
44803         initialSize: 100,
44804         minSize: 100,
44805         maxSize: 200,
44806         titlebar: true,
44807         collapsible: true,
44808         panels: [new CP("south", {title: "South", closable: true})]
44809     },
44810     center: {
44811         titlebar: true,
44812         autoScroll:true,
44813         resizeTabs: true,
44814         minTabWidth: 50,
44815         preferredTabWidth: 150,
44816         panels: [
44817             new CP("center1", {title: "Close Me", closable: true}),
44818             new CP("center2", {title: "Center Panel", closable: false})
44819         ]
44820     }
44821 }, document.body);
44822
44823 layout.getRegion("center").showPanel("center1");
44824 </code></pre>
44825  * @param config
44826  * @param targetEl
44827  */
44828 Roo.BorderLayout.create = function(config, targetEl){
44829     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44830     layout.beginUpdate();
44831     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44832     for(var j = 0, jlen = regions.length; j < jlen; j++){
44833         var lr = regions[j];
44834         if(layout.regions[lr] && config[lr].panels){
44835             var r = layout.regions[lr];
44836             var ps = config[lr].panels;
44837             layout.addTypedPanels(r, ps);
44838         }
44839     }
44840     layout.endUpdate();
44841     return layout;
44842 };
44843
44844 // private
44845 Roo.BorderLayout.RegionFactory = {
44846     // private
44847     validRegions : ["north","south","east","west","center"],
44848
44849     // private
44850     create : function(target, mgr, config){
44851         target = target.toLowerCase();
44852         if(config.lightweight || config.basic){
44853             return new Roo.BasicLayoutRegion(mgr, config, target);
44854         }
44855         switch(target){
44856             case "north":
44857                 return new Roo.NorthLayoutRegion(mgr, config);
44858             case "south":
44859                 return new Roo.SouthLayoutRegion(mgr, config);
44860             case "east":
44861                 return new Roo.EastLayoutRegion(mgr, config);
44862             case "west":
44863                 return new Roo.WestLayoutRegion(mgr, config);
44864             case "center":
44865                 return new Roo.CenterLayoutRegion(mgr, config);
44866         }
44867         throw 'Layout region "'+target+'" not supported.';
44868     }
44869 };/*
44870  * Based on:
44871  * Ext JS Library 1.1.1
44872  * Copyright(c) 2006-2007, Ext JS, LLC.
44873  *
44874  * Originally Released Under LGPL - original licence link has changed is not relivant.
44875  *
44876  * Fork - LGPL
44877  * <script type="text/javascript">
44878  */
44879  
44880 /**
44881  * @class Roo.BasicLayoutRegion
44882  * @extends Roo.util.Observable
44883  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44884  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44885  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44886  */
44887 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44888     this.mgr = mgr;
44889     this.position  = pos;
44890     this.events = {
44891         /**
44892          * @scope Roo.BasicLayoutRegion
44893          */
44894         
44895         /**
44896          * @event beforeremove
44897          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44898          * @param {Roo.LayoutRegion} this
44899          * @param {Roo.ContentPanel} panel The panel
44900          * @param {Object} e The cancel event object
44901          */
44902         "beforeremove" : true,
44903         /**
44904          * @event invalidated
44905          * Fires when the layout for this region is changed.
44906          * @param {Roo.LayoutRegion} this
44907          */
44908         "invalidated" : true,
44909         /**
44910          * @event visibilitychange
44911          * Fires when this region is shown or hidden 
44912          * @param {Roo.LayoutRegion} this
44913          * @param {Boolean} visibility true or false
44914          */
44915         "visibilitychange" : true,
44916         /**
44917          * @event paneladded
44918          * Fires when a panel is added. 
44919          * @param {Roo.LayoutRegion} this
44920          * @param {Roo.ContentPanel} panel The panel
44921          */
44922         "paneladded" : true,
44923         /**
44924          * @event panelremoved
44925          * Fires when a panel is removed. 
44926          * @param {Roo.LayoutRegion} this
44927          * @param {Roo.ContentPanel} panel The panel
44928          */
44929         "panelremoved" : true,
44930         /**
44931          * @event collapsed
44932          * Fires when this region is collapsed.
44933          * @param {Roo.LayoutRegion} this
44934          */
44935         "collapsed" : true,
44936         /**
44937          * @event expanded
44938          * Fires when this region is expanded.
44939          * @param {Roo.LayoutRegion} this
44940          */
44941         "expanded" : true,
44942         /**
44943          * @event slideshow
44944          * Fires when this region is slid into view.
44945          * @param {Roo.LayoutRegion} this
44946          */
44947         "slideshow" : true,
44948         /**
44949          * @event slidehide
44950          * Fires when this region slides out of view. 
44951          * @param {Roo.LayoutRegion} this
44952          */
44953         "slidehide" : true,
44954         /**
44955          * @event panelactivated
44956          * Fires when a panel is activated. 
44957          * @param {Roo.LayoutRegion} this
44958          * @param {Roo.ContentPanel} panel The activated panel
44959          */
44960         "panelactivated" : true,
44961         /**
44962          * @event resized
44963          * Fires when the user resizes this region. 
44964          * @param {Roo.LayoutRegion} this
44965          * @param {Number} newSize The new size (width for east/west, height for north/south)
44966          */
44967         "resized" : true
44968     };
44969     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44970     this.panels = new Roo.util.MixedCollection();
44971     this.panels.getKey = this.getPanelId.createDelegate(this);
44972     this.box = null;
44973     this.activePanel = null;
44974     // ensure listeners are added...
44975     
44976     if (config.listeners || config.events) {
44977         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44978             listeners : config.listeners || {},
44979             events : config.events || {}
44980         });
44981     }
44982     
44983     if(skipConfig !== true){
44984         this.applyConfig(config);
44985     }
44986 };
44987
44988 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44989     getPanelId : function(p){
44990         return p.getId();
44991     },
44992     
44993     applyConfig : function(config){
44994         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44995         this.config = config;
44996         
44997     },
44998     
44999     /**
45000      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45001      * the width, for horizontal (north, south) the height.
45002      * @param {Number} newSize The new width or height
45003      */
45004     resizeTo : function(newSize){
45005         var el = this.el ? this.el :
45006                  (this.activePanel ? this.activePanel.getEl() : null);
45007         if(el){
45008             switch(this.position){
45009                 case "east":
45010                 case "west":
45011                     el.setWidth(newSize);
45012                     this.fireEvent("resized", this, newSize);
45013                 break;
45014                 case "north":
45015                 case "south":
45016                     el.setHeight(newSize);
45017                     this.fireEvent("resized", this, newSize);
45018                 break;                
45019             }
45020         }
45021     },
45022     
45023     getBox : function(){
45024         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45025     },
45026     
45027     getMargins : function(){
45028         return this.margins;
45029     },
45030     
45031     updateBox : function(box){
45032         this.box = box;
45033         var el = this.activePanel.getEl();
45034         el.dom.style.left = box.x + "px";
45035         el.dom.style.top = box.y + "px";
45036         this.activePanel.setSize(box.width, box.height);
45037     },
45038     
45039     /**
45040      * Returns the container element for this region.
45041      * @return {Roo.Element}
45042      */
45043     getEl : function(){
45044         return this.activePanel;
45045     },
45046     
45047     /**
45048      * Returns true if this region is currently visible.
45049      * @return {Boolean}
45050      */
45051     isVisible : function(){
45052         return this.activePanel ? true : false;
45053     },
45054     
45055     setActivePanel : function(panel){
45056         panel = this.getPanel(panel);
45057         if(this.activePanel && this.activePanel != panel){
45058             this.activePanel.setActiveState(false);
45059             this.activePanel.getEl().setLeftTop(-10000,-10000);
45060         }
45061         this.activePanel = panel;
45062         panel.setActiveState(true);
45063         if(this.box){
45064             panel.setSize(this.box.width, this.box.height);
45065         }
45066         this.fireEvent("panelactivated", this, panel);
45067         this.fireEvent("invalidated");
45068     },
45069     
45070     /**
45071      * Show the specified panel.
45072      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45073      * @return {Roo.ContentPanel} The shown panel or null
45074      */
45075     showPanel : function(panel){
45076         if(panel = this.getPanel(panel)){
45077             this.setActivePanel(panel);
45078         }
45079         return panel;
45080     },
45081     
45082     /**
45083      * Get the active panel for this region.
45084      * @return {Roo.ContentPanel} The active panel or null
45085      */
45086     getActivePanel : function(){
45087         return this.activePanel;
45088     },
45089     
45090     /**
45091      * Add the passed ContentPanel(s)
45092      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45093      * @return {Roo.ContentPanel} The panel added (if only one was added)
45094      */
45095     add : function(panel){
45096         if(arguments.length > 1){
45097             for(var i = 0, len = arguments.length; i < len; i++) {
45098                 this.add(arguments[i]);
45099             }
45100             return null;
45101         }
45102         if(this.hasPanel(panel)){
45103             this.showPanel(panel);
45104             return panel;
45105         }
45106         var el = panel.getEl();
45107         if(el.dom.parentNode != this.mgr.el.dom){
45108             this.mgr.el.dom.appendChild(el.dom);
45109         }
45110         if(panel.setRegion){
45111             panel.setRegion(this);
45112         }
45113         this.panels.add(panel);
45114         el.setStyle("position", "absolute");
45115         if(!panel.background){
45116             this.setActivePanel(panel);
45117             if(this.config.initialSize && this.panels.getCount()==1){
45118                 this.resizeTo(this.config.initialSize);
45119             }
45120         }
45121         this.fireEvent("paneladded", this, panel);
45122         return panel;
45123     },
45124     
45125     /**
45126      * Returns true if the panel is in this region.
45127      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45128      * @return {Boolean}
45129      */
45130     hasPanel : function(panel){
45131         if(typeof panel == "object"){ // must be panel obj
45132             panel = panel.getId();
45133         }
45134         return this.getPanel(panel) ? true : false;
45135     },
45136     
45137     /**
45138      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45139      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45140      * @param {Boolean} preservePanel Overrides the config preservePanel option
45141      * @return {Roo.ContentPanel} The panel that was removed
45142      */
45143     remove : function(panel, preservePanel){
45144         panel = this.getPanel(panel);
45145         if(!panel){
45146             return null;
45147         }
45148         var e = {};
45149         this.fireEvent("beforeremove", this, panel, e);
45150         if(e.cancel === true){
45151             return null;
45152         }
45153         var panelId = panel.getId();
45154         this.panels.removeKey(panelId);
45155         return panel;
45156     },
45157     
45158     /**
45159      * Returns the panel specified or null if it's not in this region.
45160      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45161      * @return {Roo.ContentPanel}
45162      */
45163     getPanel : function(id){
45164         if(typeof id == "object"){ // must be panel obj
45165             return id;
45166         }
45167         return this.panels.get(id);
45168     },
45169     
45170     /**
45171      * Returns this regions position (north/south/east/west/center).
45172      * @return {String} 
45173      */
45174     getPosition: function(){
45175         return this.position;    
45176     }
45177 });/*
45178  * Based on:
45179  * Ext JS Library 1.1.1
45180  * Copyright(c) 2006-2007, Ext JS, LLC.
45181  *
45182  * Originally Released Under LGPL - original licence link has changed is not relivant.
45183  *
45184  * Fork - LGPL
45185  * <script type="text/javascript">
45186  */
45187  
45188 /**
45189  * @class Roo.LayoutRegion
45190  * @extends Roo.BasicLayoutRegion
45191  * This class represents a region in a layout manager.
45192  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45193  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45194  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45195  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45196  * @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})
45197  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45198  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45199  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45200  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45201  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45202  * @cfg {String}    title           The title for the region (overrides panel titles)
45203  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45204  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45205  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45206  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45207  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45208  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45209  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45210  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45211  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45212  * @cfg {Boolean}   showPin         True to show a pin button
45213  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45214  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45215  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45216  * @cfg {Number}    width           For East/West panels
45217  * @cfg {Number}    height          For North/South panels
45218  * @cfg {Boolean}   split           To show the splitter
45219  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45220  */
45221 Roo.LayoutRegion = function(mgr, config, pos){
45222     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45223     var dh = Roo.DomHelper;
45224     /** This region's container element 
45225     * @type Roo.Element */
45226     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45227     /** This region's title element 
45228     * @type Roo.Element */
45229
45230     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45231         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45232         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45233     ]}, true);
45234     this.titleEl.enableDisplayMode();
45235     /** This region's title text element 
45236     * @type HTMLElement */
45237     this.titleTextEl = this.titleEl.dom.firstChild;
45238     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45239     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45240     this.closeBtn.enableDisplayMode();
45241     this.closeBtn.on("click", this.closeClicked, this);
45242     this.closeBtn.hide();
45243
45244     this.createBody(config);
45245     this.visible = true;
45246     this.collapsed = false;
45247
45248     if(config.hideWhenEmpty){
45249         this.hide();
45250         this.on("paneladded", this.validateVisibility, this);
45251         this.on("panelremoved", this.validateVisibility, this);
45252     }
45253     this.applyConfig(config);
45254 };
45255
45256 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45257
45258     createBody : function(){
45259         /** This region's body element 
45260         * @type Roo.Element */
45261         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45262     },
45263
45264     applyConfig : function(c){
45265         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45266             var dh = Roo.DomHelper;
45267             if(c.titlebar !== false){
45268                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45269                 this.collapseBtn.on("click", this.collapse, this);
45270                 this.collapseBtn.enableDisplayMode();
45271
45272                 if(c.showPin === true || this.showPin){
45273                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45274                     this.stickBtn.enableDisplayMode();
45275                     this.stickBtn.on("click", this.expand, this);
45276                     this.stickBtn.hide();
45277                 }
45278             }
45279             /** This region's collapsed element
45280             * @type Roo.Element */
45281             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45282                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45283             ]}, true);
45284             if(c.floatable !== false){
45285                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45286                this.collapsedEl.on("click", this.collapseClick, this);
45287             }
45288
45289             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45290                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45291                    id: "message", unselectable: "on", style:{"float":"left"}});
45292                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45293              }
45294             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45295             this.expandBtn.on("click", this.expand, this);
45296         }
45297         if(this.collapseBtn){
45298             this.collapseBtn.setVisible(c.collapsible == true);
45299         }
45300         this.cmargins = c.cmargins || this.cmargins ||
45301                          (this.position == "west" || this.position == "east" ?
45302                              {top: 0, left: 2, right:2, bottom: 0} :
45303                              {top: 2, left: 0, right:0, bottom: 2});
45304         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45305         this.bottomTabs = c.tabPosition != "top";
45306         this.autoScroll = c.autoScroll || false;
45307         if(this.autoScroll){
45308             this.bodyEl.setStyle("overflow", "auto");
45309         }else{
45310             this.bodyEl.setStyle("overflow", "hidden");
45311         }
45312         //if(c.titlebar !== false){
45313             if((!c.titlebar && !c.title) || c.titlebar === false){
45314                 this.titleEl.hide();
45315             }else{
45316                 this.titleEl.show();
45317                 if(c.title){
45318                     this.titleTextEl.innerHTML = c.title;
45319                 }
45320             }
45321         //}
45322         this.duration = c.duration || .30;
45323         this.slideDuration = c.slideDuration || .45;
45324         this.config = c;
45325         if(c.collapsed){
45326             this.collapse(true);
45327         }
45328         if(c.hidden){
45329             this.hide();
45330         }
45331     },
45332     /**
45333      * Returns true if this region is currently visible.
45334      * @return {Boolean}
45335      */
45336     isVisible : function(){
45337         return this.visible;
45338     },
45339
45340     /**
45341      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45342      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45343      */
45344     setCollapsedTitle : function(title){
45345         title = title || "&#160;";
45346         if(this.collapsedTitleTextEl){
45347             this.collapsedTitleTextEl.innerHTML = title;
45348         }
45349     },
45350
45351     getBox : function(){
45352         var b;
45353         if(!this.collapsed){
45354             b = this.el.getBox(false, true);
45355         }else{
45356             b = this.collapsedEl.getBox(false, true);
45357         }
45358         return b;
45359     },
45360
45361     getMargins : function(){
45362         return this.collapsed ? this.cmargins : this.margins;
45363     },
45364
45365     highlight : function(){
45366         this.el.addClass("x-layout-panel-dragover");
45367     },
45368
45369     unhighlight : function(){
45370         this.el.removeClass("x-layout-panel-dragover");
45371     },
45372
45373     updateBox : function(box){
45374         this.box = box;
45375         if(!this.collapsed){
45376             this.el.dom.style.left = box.x + "px";
45377             this.el.dom.style.top = box.y + "px";
45378             this.updateBody(box.width, box.height);
45379         }else{
45380             this.collapsedEl.dom.style.left = box.x + "px";
45381             this.collapsedEl.dom.style.top = box.y + "px";
45382             this.collapsedEl.setSize(box.width, box.height);
45383         }
45384         if(this.tabs){
45385             this.tabs.autoSizeTabs();
45386         }
45387     },
45388
45389     updateBody : function(w, h){
45390         if(w !== null){
45391             this.el.setWidth(w);
45392             w -= this.el.getBorderWidth("rl");
45393             if(this.config.adjustments){
45394                 w += this.config.adjustments[0];
45395             }
45396         }
45397         if(h !== null){
45398             this.el.setHeight(h);
45399             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45400             h -= this.el.getBorderWidth("tb");
45401             if(this.config.adjustments){
45402                 h += this.config.adjustments[1];
45403             }
45404             this.bodyEl.setHeight(h);
45405             if(this.tabs){
45406                 h = this.tabs.syncHeight(h);
45407             }
45408         }
45409         if(this.panelSize){
45410             w = w !== null ? w : this.panelSize.width;
45411             h = h !== null ? h : this.panelSize.height;
45412         }
45413         if(this.activePanel){
45414             var el = this.activePanel.getEl();
45415             w = w !== null ? w : el.getWidth();
45416             h = h !== null ? h : el.getHeight();
45417             this.panelSize = {width: w, height: h};
45418             this.activePanel.setSize(w, h);
45419         }
45420         if(Roo.isIE && this.tabs){
45421             this.tabs.el.repaint();
45422         }
45423     },
45424
45425     /**
45426      * Returns the container element for this region.
45427      * @return {Roo.Element}
45428      */
45429     getEl : function(){
45430         return this.el;
45431     },
45432
45433     /**
45434      * Hides this region.
45435      */
45436     hide : function(){
45437         if(!this.collapsed){
45438             this.el.dom.style.left = "-2000px";
45439             this.el.hide();
45440         }else{
45441             this.collapsedEl.dom.style.left = "-2000px";
45442             this.collapsedEl.hide();
45443         }
45444         this.visible = false;
45445         this.fireEvent("visibilitychange", this, false);
45446     },
45447
45448     /**
45449      * Shows this region if it was previously hidden.
45450      */
45451     show : function(){
45452         if(!this.collapsed){
45453             this.el.show();
45454         }else{
45455             this.collapsedEl.show();
45456         }
45457         this.visible = true;
45458         this.fireEvent("visibilitychange", this, true);
45459     },
45460
45461     closeClicked : function(){
45462         if(this.activePanel){
45463             this.remove(this.activePanel);
45464         }
45465     },
45466
45467     collapseClick : function(e){
45468         if(this.isSlid){
45469            e.stopPropagation();
45470            this.slideIn();
45471         }else{
45472            e.stopPropagation();
45473            this.slideOut();
45474         }
45475     },
45476
45477     /**
45478      * Collapses this region.
45479      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45480      */
45481     collapse : function(skipAnim){
45482         if(this.collapsed) return;
45483         this.collapsed = true;
45484         if(this.split){
45485             this.split.el.hide();
45486         }
45487         if(this.config.animate && skipAnim !== true){
45488             this.fireEvent("invalidated", this);
45489             this.animateCollapse();
45490         }else{
45491             this.el.setLocation(-20000,-20000);
45492             this.el.hide();
45493             this.collapsedEl.show();
45494             this.fireEvent("collapsed", this);
45495             this.fireEvent("invalidated", this);
45496         }
45497     },
45498
45499     animateCollapse : function(){
45500         // overridden
45501     },
45502
45503     /**
45504      * Expands this region if it was previously collapsed.
45505      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45506      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45507      */
45508     expand : function(e, skipAnim){
45509         if(e) e.stopPropagation();
45510         if(!this.collapsed || this.el.hasActiveFx()) return;
45511         if(this.isSlid){
45512             this.afterSlideIn();
45513             skipAnim = true;
45514         }
45515         this.collapsed = false;
45516         if(this.config.animate && skipAnim !== true){
45517             this.animateExpand();
45518         }else{
45519             this.el.show();
45520             if(this.split){
45521                 this.split.el.show();
45522             }
45523             this.collapsedEl.setLocation(-2000,-2000);
45524             this.collapsedEl.hide();
45525             this.fireEvent("invalidated", this);
45526             this.fireEvent("expanded", this);
45527         }
45528     },
45529
45530     animateExpand : function(){
45531         // overridden
45532     },
45533
45534     initTabs : function()
45535     {
45536         this.bodyEl.setStyle("overflow", "hidden");
45537         var ts = new Roo.TabPanel(
45538                 this.bodyEl.dom,
45539                 {
45540                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45541                     disableTooltips: this.config.disableTabTips,
45542                     toolbar : this.config.toolbar
45543                 }
45544         );
45545         if(this.config.hideTabs){
45546             ts.stripWrap.setDisplayed(false);
45547         }
45548         this.tabs = ts;
45549         ts.resizeTabs = this.config.resizeTabs === true;
45550         ts.minTabWidth = this.config.minTabWidth || 40;
45551         ts.maxTabWidth = this.config.maxTabWidth || 250;
45552         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45553         ts.monitorResize = false;
45554         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45555         ts.bodyEl.addClass('x-layout-tabs-body');
45556         this.panels.each(this.initPanelAsTab, this);
45557     },
45558
45559     initPanelAsTab : function(panel){
45560         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45561                     this.config.closeOnTab && panel.isClosable());
45562         if(panel.tabTip !== undefined){
45563             ti.setTooltip(panel.tabTip);
45564         }
45565         ti.on("activate", function(){
45566               this.setActivePanel(panel);
45567         }, this);
45568         if(this.config.closeOnTab){
45569             ti.on("beforeclose", function(t, e){
45570                 e.cancel = true;
45571                 this.remove(panel);
45572             }, this);
45573         }
45574         return ti;
45575     },
45576
45577     updatePanelTitle : function(panel, title){
45578         if(this.activePanel == panel){
45579             this.updateTitle(title);
45580         }
45581         if(this.tabs){
45582             var ti = this.tabs.getTab(panel.getEl().id);
45583             ti.setText(title);
45584             if(panel.tabTip !== undefined){
45585                 ti.setTooltip(panel.tabTip);
45586             }
45587         }
45588     },
45589
45590     updateTitle : function(title){
45591         if(this.titleTextEl && !this.config.title){
45592             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45593         }
45594     },
45595
45596     setActivePanel : function(panel){
45597         panel = this.getPanel(panel);
45598         if(this.activePanel && this.activePanel != panel){
45599             this.activePanel.setActiveState(false);
45600         }
45601         this.activePanel = panel;
45602         panel.setActiveState(true);
45603         if(this.panelSize){
45604             panel.setSize(this.panelSize.width, this.panelSize.height);
45605         }
45606         if(this.closeBtn){
45607             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45608         }
45609         this.updateTitle(panel.getTitle());
45610         if(this.tabs){
45611             this.fireEvent("invalidated", this);
45612         }
45613         this.fireEvent("panelactivated", this, panel);
45614     },
45615
45616     /**
45617      * Shows the specified panel.
45618      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45619      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45620      */
45621     showPanel : function(panel){
45622         if(panel = this.getPanel(panel)){
45623             if(this.tabs){
45624                 var tab = this.tabs.getTab(panel.getEl().id);
45625                 if(tab.isHidden()){
45626                     this.tabs.unhideTab(tab.id);
45627                 }
45628                 tab.activate();
45629             }else{
45630                 this.setActivePanel(panel);
45631             }
45632         }
45633         return panel;
45634     },
45635
45636     /**
45637      * Get the active panel for this region.
45638      * @return {Roo.ContentPanel} The active panel or null
45639      */
45640     getActivePanel : function(){
45641         return this.activePanel;
45642     },
45643
45644     validateVisibility : function(){
45645         if(this.panels.getCount() < 1){
45646             this.updateTitle("&#160;");
45647             this.closeBtn.hide();
45648             this.hide();
45649         }else{
45650             if(!this.isVisible()){
45651                 this.show();
45652             }
45653         }
45654     },
45655
45656     /**
45657      * Adds the passed ContentPanel(s) to this region.
45658      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45659      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45660      */
45661     add : function(panel){
45662         if(arguments.length > 1){
45663             for(var i = 0, len = arguments.length; i < len; i++) {
45664                 this.add(arguments[i]);
45665             }
45666             return null;
45667         }
45668         if(this.hasPanel(panel)){
45669             this.showPanel(panel);
45670             return panel;
45671         }
45672         panel.setRegion(this);
45673         this.panels.add(panel);
45674         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45675             this.bodyEl.dom.appendChild(panel.getEl().dom);
45676             if(panel.background !== true){
45677                 this.setActivePanel(panel);
45678             }
45679             this.fireEvent("paneladded", this, panel);
45680             return panel;
45681         }
45682         if(!this.tabs){
45683             this.initTabs();
45684         }else{
45685             this.initPanelAsTab(panel);
45686         }
45687         if(panel.background !== true){
45688             this.tabs.activate(panel.getEl().id);
45689         }
45690         this.fireEvent("paneladded", this, panel);
45691         return panel;
45692     },
45693
45694     /**
45695      * Hides the tab for the specified panel.
45696      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45697      */
45698     hidePanel : function(panel){
45699         if(this.tabs && (panel = this.getPanel(panel))){
45700             this.tabs.hideTab(panel.getEl().id);
45701         }
45702     },
45703
45704     /**
45705      * Unhides the tab for a previously hidden panel.
45706      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45707      */
45708     unhidePanel : function(panel){
45709         if(this.tabs && (panel = this.getPanel(panel))){
45710             this.tabs.unhideTab(panel.getEl().id);
45711         }
45712     },
45713
45714     clearPanels : function(){
45715         while(this.panels.getCount() > 0){
45716              this.remove(this.panels.first());
45717         }
45718     },
45719
45720     /**
45721      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45722      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45723      * @param {Boolean} preservePanel Overrides the config preservePanel option
45724      * @return {Roo.ContentPanel} The panel that was removed
45725      */
45726     remove : function(panel, preservePanel){
45727         panel = this.getPanel(panel);
45728         if(!panel){
45729             return null;
45730         }
45731         var e = {};
45732         this.fireEvent("beforeremove", this, panel, e);
45733         if(e.cancel === true){
45734             return null;
45735         }
45736         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45737         var panelId = panel.getId();
45738         this.panels.removeKey(panelId);
45739         if(preservePanel){
45740             document.body.appendChild(panel.getEl().dom);
45741         }
45742         if(this.tabs){
45743             this.tabs.removeTab(panel.getEl().id);
45744         }else if (!preservePanel){
45745             this.bodyEl.dom.removeChild(panel.getEl().dom);
45746         }
45747         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45748             var p = this.panels.first();
45749             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45750             tempEl.appendChild(p.getEl().dom);
45751             this.bodyEl.update("");
45752             this.bodyEl.dom.appendChild(p.getEl().dom);
45753             tempEl = null;
45754             this.updateTitle(p.getTitle());
45755             this.tabs = null;
45756             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45757             this.setActivePanel(p);
45758         }
45759         panel.setRegion(null);
45760         if(this.activePanel == panel){
45761             this.activePanel = null;
45762         }
45763         if(this.config.autoDestroy !== false && preservePanel !== true){
45764             try{panel.destroy();}catch(e){}
45765         }
45766         this.fireEvent("panelremoved", this, panel);
45767         return panel;
45768     },
45769
45770     /**
45771      * Returns the TabPanel component used by this region
45772      * @return {Roo.TabPanel}
45773      */
45774     getTabs : function(){
45775         return this.tabs;
45776     },
45777
45778     createTool : function(parentEl, className){
45779         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45780             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45781         btn.addClassOnOver("x-layout-tools-button-over");
45782         return btn;
45783     }
45784 });/*
45785  * Based on:
45786  * Ext JS Library 1.1.1
45787  * Copyright(c) 2006-2007, Ext JS, LLC.
45788  *
45789  * Originally Released Under LGPL - original licence link has changed is not relivant.
45790  *
45791  * Fork - LGPL
45792  * <script type="text/javascript">
45793  */
45794  
45795
45796
45797 /**
45798  * @class Roo.SplitLayoutRegion
45799  * @extends Roo.LayoutRegion
45800  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45801  */
45802 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45803     this.cursor = cursor;
45804     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45805 };
45806
45807 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45808     splitTip : "Drag to resize.",
45809     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45810     useSplitTips : false,
45811
45812     applyConfig : function(config){
45813         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45814         if(config.split){
45815             if(!this.split){
45816                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45817                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45818                 /** The SplitBar for this region 
45819                 * @type Roo.SplitBar */
45820                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45821                 this.split.on("moved", this.onSplitMove, this);
45822                 this.split.useShim = config.useShim === true;
45823                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45824                 if(this.useSplitTips){
45825                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45826                 }
45827                 if(config.collapsible){
45828                     this.split.el.on("dblclick", this.collapse,  this);
45829                 }
45830             }
45831             if(typeof config.minSize != "undefined"){
45832                 this.split.minSize = config.minSize;
45833             }
45834             if(typeof config.maxSize != "undefined"){
45835                 this.split.maxSize = config.maxSize;
45836             }
45837             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45838                 this.hideSplitter();
45839             }
45840         }
45841     },
45842
45843     getHMaxSize : function(){
45844          var cmax = this.config.maxSize || 10000;
45845          var center = this.mgr.getRegion("center");
45846          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45847     },
45848
45849     getVMaxSize : function(){
45850          var cmax = this.config.maxSize || 10000;
45851          var center = this.mgr.getRegion("center");
45852          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45853     },
45854
45855     onSplitMove : function(split, newSize){
45856         this.fireEvent("resized", this, newSize);
45857     },
45858     
45859     /** 
45860      * Returns the {@link Roo.SplitBar} for this region.
45861      * @return {Roo.SplitBar}
45862      */
45863     getSplitBar : function(){
45864         return this.split;
45865     },
45866     
45867     hide : function(){
45868         this.hideSplitter();
45869         Roo.SplitLayoutRegion.superclass.hide.call(this);
45870     },
45871
45872     hideSplitter : function(){
45873         if(this.split){
45874             this.split.el.setLocation(-2000,-2000);
45875             this.split.el.hide();
45876         }
45877     },
45878
45879     show : function(){
45880         if(this.split){
45881             this.split.el.show();
45882         }
45883         Roo.SplitLayoutRegion.superclass.show.call(this);
45884     },
45885     
45886     beforeSlide: function(){
45887         if(Roo.isGecko){// firefox overflow auto bug workaround
45888             this.bodyEl.clip();
45889             if(this.tabs) this.tabs.bodyEl.clip();
45890             if(this.activePanel){
45891                 this.activePanel.getEl().clip();
45892                 
45893                 if(this.activePanel.beforeSlide){
45894                     this.activePanel.beforeSlide();
45895                 }
45896             }
45897         }
45898     },
45899     
45900     afterSlide : function(){
45901         if(Roo.isGecko){// firefox overflow auto bug workaround
45902             this.bodyEl.unclip();
45903             if(this.tabs) this.tabs.bodyEl.unclip();
45904             if(this.activePanel){
45905                 this.activePanel.getEl().unclip();
45906                 if(this.activePanel.afterSlide){
45907                     this.activePanel.afterSlide();
45908                 }
45909             }
45910         }
45911     },
45912
45913     initAutoHide : function(){
45914         if(this.autoHide !== false){
45915             if(!this.autoHideHd){
45916                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45917                 this.autoHideHd = {
45918                     "mouseout": function(e){
45919                         if(!e.within(this.el, true)){
45920                             st.delay(500);
45921                         }
45922                     },
45923                     "mouseover" : function(e){
45924                         st.cancel();
45925                     },
45926                     scope : this
45927                 };
45928             }
45929             this.el.on(this.autoHideHd);
45930         }
45931     },
45932
45933     clearAutoHide : function(){
45934         if(this.autoHide !== false){
45935             this.el.un("mouseout", this.autoHideHd.mouseout);
45936             this.el.un("mouseover", this.autoHideHd.mouseover);
45937         }
45938     },
45939
45940     clearMonitor : function(){
45941         Roo.get(document).un("click", this.slideInIf, this);
45942     },
45943
45944     // these names are backwards but not changed for compat
45945     slideOut : function(){
45946         if(this.isSlid || this.el.hasActiveFx()){
45947             return;
45948         }
45949         this.isSlid = true;
45950         if(this.collapseBtn){
45951             this.collapseBtn.hide();
45952         }
45953         this.closeBtnState = this.closeBtn.getStyle('display');
45954         this.closeBtn.hide();
45955         if(this.stickBtn){
45956             this.stickBtn.show();
45957         }
45958         this.el.show();
45959         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45960         this.beforeSlide();
45961         this.el.setStyle("z-index", 10001);
45962         this.el.slideIn(this.getSlideAnchor(), {
45963             callback: function(){
45964                 this.afterSlide();
45965                 this.initAutoHide();
45966                 Roo.get(document).on("click", this.slideInIf, this);
45967                 this.fireEvent("slideshow", this);
45968             },
45969             scope: this,
45970             block: true
45971         });
45972     },
45973
45974     afterSlideIn : function(){
45975         this.clearAutoHide();
45976         this.isSlid = false;
45977         this.clearMonitor();
45978         this.el.setStyle("z-index", "");
45979         if(this.collapseBtn){
45980             this.collapseBtn.show();
45981         }
45982         this.closeBtn.setStyle('display', this.closeBtnState);
45983         if(this.stickBtn){
45984             this.stickBtn.hide();
45985         }
45986         this.fireEvent("slidehide", this);
45987     },
45988
45989     slideIn : function(cb){
45990         if(!this.isSlid || this.el.hasActiveFx()){
45991             Roo.callback(cb);
45992             return;
45993         }
45994         this.isSlid = false;
45995         this.beforeSlide();
45996         this.el.slideOut(this.getSlideAnchor(), {
45997             callback: function(){
45998                 this.el.setLeftTop(-10000, -10000);
45999                 this.afterSlide();
46000                 this.afterSlideIn();
46001                 Roo.callback(cb);
46002             },
46003             scope: this,
46004             block: true
46005         });
46006     },
46007     
46008     slideInIf : function(e){
46009         if(!e.within(this.el)){
46010             this.slideIn();
46011         }
46012     },
46013
46014     animateCollapse : function(){
46015         this.beforeSlide();
46016         this.el.setStyle("z-index", 20000);
46017         var anchor = this.getSlideAnchor();
46018         this.el.slideOut(anchor, {
46019             callback : function(){
46020                 this.el.setStyle("z-index", "");
46021                 this.collapsedEl.slideIn(anchor, {duration:.3});
46022                 this.afterSlide();
46023                 this.el.setLocation(-10000,-10000);
46024                 this.el.hide();
46025                 this.fireEvent("collapsed", this);
46026             },
46027             scope: this,
46028             block: true
46029         });
46030     },
46031
46032     animateExpand : function(){
46033         this.beforeSlide();
46034         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46035         this.el.setStyle("z-index", 20000);
46036         this.collapsedEl.hide({
46037             duration:.1
46038         });
46039         this.el.slideIn(this.getSlideAnchor(), {
46040             callback : function(){
46041                 this.el.setStyle("z-index", "");
46042                 this.afterSlide();
46043                 if(this.split){
46044                     this.split.el.show();
46045                 }
46046                 this.fireEvent("invalidated", this);
46047                 this.fireEvent("expanded", this);
46048             },
46049             scope: this,
46050             block: true
46051         });
46052     },
46053
46054     anchors : {
46055         "west" : "left",
46056         "east" : "right",
46057         "north" : "top",
46058         "south" : "bottom"
46059     },
46060
46061     sanchors : {
46062         "west" : "l",
46063         "east" : "r",
46064         "north" : "t",
46065         "south" : "b"
46066     },
46067
46068     canchors : {
46069         "west" : "tl-tr",
46070         "east" : "tr-tl",
46071         "north" : "tl-bl",
46072         "south" : "bl-tl"
46073     },
46074
46075     getAnchor : function(){
46076         return this.anchors[this.position];
46077     },
46078
46079     getCollapseAnchor : function(){
46080         return this.canchors[this.position];
46081     },
46082
46083     getSlideAnchor : function(){
46084         return this.sanchors[this.position];
46085     },
46086
46087     getAlignAdj : function(){
46088         var cm = this.cmargins;
46089         switch(this.position){
46090             case "west":
46091                 return [0, 0];
46092             break;
46093             case "east":
46094                 return [0, 0];
46095             break;
46096             case "north":
46097                 return [0, 0];
46098             break;
46099             case "south":
46100                 return [0, 0];
46101             break;
46102         }
46103     },
46104
46105     getExpandAdj : function(){
46106         var c = this.collapsedEl, cm = this.cmargins;
46107         switch(this.position){
46108             case "west":
46109                 return [-(cm.right+c.getWidth()+cm.left), 0];
46110             break;
46111             case "east":
46112                 return [cm.right+c.getWidth()+cm.left, 0];
46113             break;
46114             case "north":
46115                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46116             break;
46117             case "south":
46118                 return [0, cm.top+cm.bottom+c.getHeight()];
46119             break;
46120         }
46121     }
46122 });/*
46123  * Based on:
46124  * Ext JS Library 1.1.1
46125  * Copyright(c) 2006-2007, Ext JS, LLC.
46126  *
46127  * Originally Released Under LGPL - original licence link has changed is not relivant.
46128  *
46129  * Fork - LGPL
46130  * <script type="text/javascript">
46131  */
46132 /*
46133  * These classes are private internal classes
46134  */
46135 Roo.CenterLayoutRegion = function(mgr, config){
46136     Roo.LayoutRegion.call(this, mgr, config, "center");
46137     this.visible = true;
46138     this.minWidth = config.minWidth || 20;
46139     this.minHeight = config.minHeight || 20;
46140 };
46141
46142 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46143     hide : function(){
46144         // center panel can't be hidden
46145     },
46146     
46147     show : function(){
46148         // center panel can't be hidden
46149     },
46150     
46151     getMinWidth: function(){
46152         return this.minWidth;
46153     },
46154     
46155     getMinHeight: function(){
46156         return this.minHeight;
46157     }
46158 });
46159
46160
46161 Roo.NorthLayoutRegion = function(mgr, config){
46162     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46163     if(this.split){
46164         this.split.placement = Roo.SplitBar.TOP;
46165         this.split.orientation = Roo.SplitBar.VERTICAL;
46166         this.split.el.addClass("x-layout-split-v");
46167     }
46168     var size = config.initialSize || config.height;
46169     if(typeof size != "undefined"){
46170         this.el.setHeight(size);
46171     }
46172 };
46173 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46174     orientation: Roo.SplitBar.VERTICAL,
46175     getBox : function(){
46176         if(this.collapsed){
46177             return this.collapsedEl.getBox();
46178         }
46179         var box = this.el.getBox();
46180         if(this.split){
46181             box.height += this.split.el.getHeight();
46182         }
46183         return box;
46184     },
46185     
46186     updateBox : function(box){
46187         if(this.split && !this.collapsed){
46188             box.height -= this.split.el.getHeight();
46189             this.split.el.setLeft(box.x);
46190             this.split.el.setTop(box.y+box.height);
46191             this.split.el.setWidth(box.width);
46192         }
46193         if(this.collapsed){
46194             this.updateBody(box.width, null);
46195         }
46196         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46197     }
46198 });
46199
46200 Roo.SouthLayoutRegion = function(mgr, config){
46201     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46202     if(this.split){
46203         this.split.placement = Roo.SplitBar.BOTTOM;
46204         this.split.orientation = Roo.SplitBar.VERTICAL;
46205         this.split.el.addClass("x-layout-split-v");
46206     }
46207     var size = config.initialSize || config.height;
46208     if(typeof size != "undefined"){
46209         this.el.setHeight(size);
46210     }
46211 };
46212 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46213     orientation: Roo.SplitBar.VERTICAL,
46214     getBox : function(){
46215         if(this.collapsed){
46216             return this.collapsedEl.getBox();
46217         }
46218         var box = this.el.getBox();
46219         if(this.split){
46220             var sh = this.split.el.getHeight();
46221             box.height += sh;
46222             box.y -= sh;
46223         }
46224         return box;
46225     },
46226     
46227     updateBox : function(box){
46228         if(this.split && !this.collapsed){
46229             var sh = this.split.el.getHeight();
46230             box.height -= sh;
46231             box.y += sh;
46232             this.split.el.setLeft(box.x);
46233             this.split.el.setTop(box.y-sh);
46234             this.split.el.setWidth(box.width);
46235         }
46236         if(this.collapsed){
46237             this.updateBody(box.width, null);
46238         }
46239         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46240     }
46241 });
46242
46243 Roo.EastLayoutRegion = function(mgr, config){
46244     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46245     if(this.split){
46246         this.split.placement = Roo.SplitBar.RIGHT;
46247         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46248         this.split.el.addClass("x-layout-split-h");
46249     }
46250     var size = config.initialSize || config.width;
46251     if(typeof size != "undefined"){
46252         this.el.setWidth(size);
46253     }
46254 };
46255 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46256     orientation: Roo.SplitBar.HORIZONTAL,
46257     getBox : function(){
46258         if(this.collapsed){
46259             return this.collapsedEl.getBox();
46260         }
46261         var box = this.el.getBox();
46262         if(this.split){
46263             var sw = this.split.el.getWidth();
46264             box.width += sw;
46265             box.x -= sw;
46266         }
46267         return box;
46268     },
46269
46270     updateBox : function(box){
46271         if(this.split && !this.collapsed){
46272             var sw = this.split.el.getWidth();
46273             box.width -= sw;
46274             this.split.el.setLeft(box.x);
46275             this.split.el.setTop(box.y);
46276             this.split.el.setHeight(box.height);
46277             box.x += sw;
46278         }
46279         if(this.collapsed){
46280             this.updateBody(null, box.height);
46281         }
46282         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46283     }
46284 });
46285
46286 Roo.WestLayoutRegion = function(mgr, config){
46287     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46288     if(this.split){
46289         this.split.placement = Roo.SplitBar.LEFT;
46290         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46291         this.split.el.addClass("x-layout-split-h");
46292     }
46293     var size = config.initialSize || config.width;
46294     if(typeof size != "undefined"){
46295         this.el.setWidth(size);
46296     }
46297 };
46298 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46299     orientation: Roo.SplitBar.HORIZONTAL,
46300     getBox : function(){
46301         if(this.collapsed){
46302             return this.collapsedEl.getBox();
46303         }
46304         var box = this.el.getBox();
46305         if(this.split){
46306             box.width += this.split.el.getWidth();
46307         }
46308         return box;
46309     },
46310     
46311     updateBox : function(box){
46312         if(this.split && !this.collapsed){
46313             var sw = this.split.el.getWidth();
46314             box.width -= sw;
46315             this.split.el.setLeft(box.x+box.width);
46316             this.split.el.setTop(box.y);
46317             this.split.el.setHeight(box.height);
46318         }
46319         if(this.collapsed){
46320             this.updateBody(null, box.height);
46321         }
46322         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46323     }
46324 });
46325 /*
46326  * Based on:
46327  * Ext JS Library 1.1.1
46328  * Copyright(c) 2006-2007, Ext JS, LLC.
46329  *
46330  * Originally Released Under LGPL - original licence link has changed is not relivant.
46331  *
46332  * Fork - LGPL
46333  * <script type="text/javascript">
46334  */
46335  
46336  
46337 /*
46338  * Private internal class for reading and applying state
46339  */
46340 Roo.LayoutStateManager = function(layout){
46341      // default empty state
46342      this.state = {
46343         north: {},
46344         south: {},
46345         east: {},
46346         west: {}       
46347     };
46348 };
46349
46350 Roo.LayoutStateManager.prototype = {
46351     init : function(layout, provider){
46352         this.provider = provider;
46353         var state = provider.get(layout.id+"-layout-state");
46354         if(state){
46355             var wasUpdating = layout.isUpdating();
46356             if(!wasUpdating){
46357                 layout.beginUpdate();
46358             }
46359             for(var key in state){
46360                 if(typeof state[key] != "function"){
46361                     var rstate = state[key];
46362                     var r = layout.getRegion(key);
46363                     if(r && rstate){
46364                         if(rstate.size){
46365                             r.resizeTo(rstate.size);
46366                         }
46367                         if(rstate.collapsed == true){
46368                             r.collapse(true);
46369                         }else{
46370                             r.expand(null, true);
46371                         }
46372                     }
46373                 }
46374             }
46375             if(!wasUpdating){
46376                 layout.endUpdate();
46377             }
46378             this.state = state; 
46379         }
46380         this.layout = layout;
46381         layout.on("regionresized", this.onRegionResized, this);
46382         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46383         layout.on("regionexpanded", this.onRegionExpanded, this);
46384     },
46385     
46386     storeState : function(){
46387         this.provider.set(this.layout.id+"-layout-state", this.state);
46388     },
46389     
46390     onRegionResized : function(region, newSize){
46391         this.state[region.getPosition()].size = newSize;
46392         this.storeState();
46393     },
46394     
46395     onRegionCollapsed : function(region){
46396         this.state[region.getPosition()].collapsed = true;
46397         this.storeState();
46398     },
46399     
46400     onRegionExpanded : function(region){
46401         this.state[region.getPosition()].collapsed = false;
46402         this.storeState();
46403     }
46404 };/*
46405  * Based on:
46406  * Ext JS Library 1.1.1
46407  * Copyright(c) 2006-2007, Ext JS, LLC.
46408  *
46409  * Originally Released Under LGPL - original licence link has changed is not relivant.
46410  *
46411  * Fork - LGPL
46412  * <script type="text/javascript">
46413  */
46414 /**
46415  * @class Roo.ContentPanel
46416  * @extends Roo.util.Observable
46417  * A basic ContentPanel element.
46418  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46419  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46420  * @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
46421  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46422  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46423  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46424  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46425  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46426  * @cfg {String} title          The title for this panel
46427  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46428  * @cfg {String} url            Calls {@link #setUrl} with this value
46429  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46430  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46431  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46432  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46433
46434  * @constructor
46435  * Create a new ContentPanel.
46436  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46437  * @param {String/Object} config A string to set only the title or a config object
46438  * @param {String} content (optional) Set the HTML content for this panel
46439  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46440  */
46441 Roo.ContentPanel = function(el, config, content){
46442     
46443      
46444     /*
46445     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46446         config = el;
46447         el = Roo.id();
46448     }
46449     if (config && config.parentLayout) { 
46450         el = config.parentLayout.el.createChild(); 
46451     }
46452     */
46453     if(el.autoCreate){ // xtype is available if this is called from factory
46454         config = el;
46455         el = Roo.id();
46456     }
46457     this.el = Roo.get(el);
46458     if(!this.el && config && config.autoCreate){
46459         if(typeof config.autoCreate == "object"){
46460             if(!config.autoCreate.id){
46461                 config.autoCreate.id = config.id||el;
46462             }
46463             this.el = Roo.DomHelper.append(document.body,
46464                         config.autoCreate, true);
46465         }else{
46466             this.el = Roo.DomHelper.append(document.body,
46467                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46468         }
46469     }
46470     this.closable = false;
46471     this.loaded = false;
46472     this.active = false;
46473     if(typeof config == "string"){
46474         this.title = config;
46475     }else{
46476         Roo.apply(this, config);
46477     }
46478     
46479     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46480         this.wrapEl = this.el.wrap();
46481         this.toolbar.container = this.el.insertSibling(false, 'before');
46482         this.toolbar = new Roo.Toolbar(this.toolbar);
46483     }
46484     
46485     
46486     
46487     if(this.resizeEl){
46488         this.resizeEl = Roo.get(this.resizeEl, true);
46489     }else{
46490         this.resizeEl = this.el;
46491     }
46492     this.addEvents({
46493         /**
46494          * @event activate
46495          * Fires when this panel is activated. 
46496          * @param {Roo.ContentPanel} this
46497          */
46498         "activate" : true,
46499         /**
46500          * @event deactivate
46501          * Fires when this panel is activated. 
46502          * @param {Roo.ContentPanel} this
46503          */
46504         "deactivate" : true,
46505
46506         /**
46507          * @event resize
46508          * Fires when this panel is resized if fitToFrame is true.
46509          * @param {Roo.ContentPanel} this
46510          * @param {Number} width The width after any component adjustments
46511          * @param {Number} height The height after any component adjustments
46512          */
46513         "resize" : true,
46514         
46515          /**
46516          * @event render
46517          * Fires when this tab is created
46518          * @param {Roo.ContentPanel} this
46519          */
46520         "render" : true
46521         
46522         
46523         
46524     });
46525     if(this.autoScroll){
46526         this.resizeEl.setStyle("overflow", "auto");
46527     } else {
46528         // fix randome scrolling
46529         this.el.on('scroll', function() {
46530             Roo.log('fix random scolling');
46531             this.scrollTo('top',0); 
46532         });
46533     }
46534     content = content || this.content;
46535     if(content){
46536         this.setContent(content);
46537     }
46538     if(config && config.url){
46539         this.setUrl(this.url, this.params, this.loadOnce);
46540     }
46541     
46542     
46543     
46544     Roo.ContentPanel.superclass.constructor.call(this);
46545     
46546     this.fireEvent('render', this);
46547 };
46548
46549 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46550     tabTip:'',
46551     setRegion : function(region){
46552         this.region = region;
46553         if(region){
46554            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46555         }else{
46556            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46557         } 
46558     },
46559     
46560     /**
46561      * Returns the toolbar for this Panel if one was configured. 
46562      * @return {Roo.Toolbar} 
46563      */
46564     getToolbar : function(){
46565         return this.toolbar;
46566     },
46567     
46568     setActiveState : function(active){
46569         this.active = active;
46570         if(!active){
46571             this.fireEvent("deactivate", this);
46572         }else{
46573             this.fireEvent("activate", this);
46574         }
46575     },
46576     /**
46577      * Updates this panel's element
46578      * @param {String} content The new content
46579      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46580     */
46581     setContent : function(content, loadScripts){
46582         this.el.update(content, loadScripts);
46583     },
46584
46585     ignoreResize : function(w, h){
46586         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46587             return true;
46588         }else{
46589             this.lastSize = {width: w, height: h};
46590             return false;
46591         }
46592     },
46593     /**
46594      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46595      * @return {Roo.UpdateManager} The UpdateManager
46596      */
46597     getUpdateManager : function(){
46598         return this.el.getUpdateManager();
46599     },
46600      /**
46601      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46602      * @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:
46603 <pre><code>
46604 panel.load({
46605     url: "your-url.php",
46606     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46607     callback: yourFunction,
46608     scope: yourObject, //(optional scope)
46609     discardUrl: false,
46610     nocache: false,
46611     text: "Loading...",
46612     timeout: 30,
46613     scripts: false
46614 });
46615 </code></pre>
46616      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46617      * 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.
46618      * @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}
46619      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46620      * @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.
46621      * @return {Roo.ContentPanel} this
46622      */
46623     load : function(){
46624         var um = this.el.getUpdateManager();
46625         um.update.apply(um, arguments);
46626         return this;
46627     },
46628
46629
46630     /**
46631      * 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.
46632      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46633      * @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)
46634      * @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)
46635      * @return {Roo.UpdateManager} The UpdateManager
46636      */
46637     setUrl : function(url, params, loadOnce){
46638         if(this.refreshDelegate){
46639             this.removeListener("activate", this.refreshDelegate);
46640         }
46641         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46642         this.on("activate", this.refreshDelegate);
46643         return this.el.getUpdateManager();
46644     },
46645     
46646     _handleRefresh : function(url, params, loadOnce){
46647         if(!loadOnce || !this.loaded){
46648             var updater = this.el.getUpdateManager();
46649             updater.update(url, params, this._setLoaded.createDelegate(this));
46650         }
46651     },
46652     
46653     _setLoaded : function(){
46654         this.loaded = true;
46655     }, 
46656     
46657     /**
46658      * Returns this panel's id
46659      * @return {String} 
46660      */
46661     getId : function(){
46662         return this.el.id;
46663     },
46664     
46665     /** 
46666      * Returns this panel's element - used by regiosn to add.
46667      * @return {Roo.Element} 
46668      */
46669     getEl : function(){
46670         return this.wrapEl || this.el;
46671     },
46672     
46673     adjustForComponents : function(width, height){
46674         if(this.resizeEl != this.el){
46675             width -= this.el.getFrameWidth('lr');
46676             height -= this.el.getFrameWidth('tb');
46677         }
46678         if(this.toolbar){
46679             var te = this.toolbar.getEl();
46680             height -= te.getHeight();
46681             te.setWidth(width);
46682         }
46683         if(this.adjustments){
46684             width += this.adjustments[0];
46685             height += this.adjustments[1];
46686         }
46687         return {"width": width, "height": height};
46688     },
46689     
46690     setSize : function(width, height){
46691         if(this.fitToFrame && !this.ignoreResize(width, height)){
46692             if(this.fitContainer && this.resizeEl != this.el){
46693                 this.el.setSize(width, height);
46694             }
46695             var size = this.adjustForComponents(width, height);
46696             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46697             this.fireEvent('resize', this, size.width, size.height);
46698         }
46699     },
46700     
46701     /**
46702      * Returns this panel's title
46703      * @return {String} 
46704      */
46705     getTitle : function(){
46706         return this.title;
46707     },
46708     
46709     /**
46710      * Set this panel's title
46711      * @param {String} title
46712      */
46713     setTitle : function(title){
46714         this.title = title;
46715         if(this.region){
46716             this.region.updatePanelTitle(this, title);
46717         }
46718     },
46719     
46720     /**
46721      * Returns true is this panel was configured to be closable
46722      * @return {Boolean} 
46723      */
46724     isClosable : function(){
46725         return this.closable;
46726     },
46727     
46728     beforeSlide : function(){
46729         this.el.clip();
46730         this.resizeEl.clip();
46731     },
46732     
46733     afterSlide : function(){
46734         this.el.unclip();
46735         this.resizeEl.unclip();
46736     },
46737     
46738     /**
46739      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46740      *   Will fail silently if the {@link #setUrl} method has not been called.
46741      *   This does not activate the panel, just updates its content.
46742      */
46743     refresh : function(){
46744         if(this.refreshDelegate){
46745            this.loaded = false;
46746            this.refreshDelegate();
46747         }
46748     },
46749     
46750     /**
46751      * Destroys this panel
46752      */
46753     destroy : function(){
46754         this.el.removeAllListeners();
46755         var tempEl = document.createElement("span");
46756         tempEl.appendChild(this.el.dom);
46757         tempEl.innerHTML = "";
46758         this.el.remove();
46759         this.el = null;
46760     },
46761     
46762     /**
46763      * form - if the content panel contains a form - this is a reference to it.
46764      * @type {Roo.form.Form}
46765      */
46766     form : false,
46767     /**
46768      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46769      *    This contains a reference to it.
46770      * @type {Roo.View}
46771      */
46772     view : false,
46773     
46774       /**
46775      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46776      * <pre><code>
46777
46778 layout.addxtype({
46779        xtype : 'Form',
46780        items: [ .... ]
46781    }
46782 );
46783
46784 </code></pre>
46785      * @param {Object} cfg Xtype definition of item to add.
46786      */
46787     
46788     addxtype : function(cfg) {
46789         // add form..
46790         if (cfg.xtype.match(/^Form$/)) {
46791             var el = this.el.createChild();
46792
46793             this.form = new  Roo.form.Form(cfg);
46794             
46795             
46796             if ( this.form.allItems.length) this.form.render(el.dom);
46797             return this.form;
46798         }
46799         // should only have one of theses..
46800         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46801             // views..
46802             cfg.el = this.el.appendChild(document.createElement("div"));
46803             // factory?
46804             
46805             var ret = new Roo.factory(cfg);
46806             ret.render && ret.render(false, ''); // render blank..
46807             this.view = ret;
46808             return ret;
46809         }
46810         return false;
46811     }
46812 });
46813
46814 /**
46815  * @class Roo.GridPanel
46816  * @extends Roo.ContentPanel
46817  * @constructor
46818  * Create a new GridPanel.
46819  * @param {Roo.grid.Grid} grid The grid for this panel
46820  * @param {String/Object} config A string to set only the panel's title, or a config object
46821  */
46822 Roo.GridPanel = function(grid, config){
46823     
46824   
46825     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46826         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46827         
46828     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46829     
46830     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46831     
46832     if(this.toolbar){
46833         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46834     }
46835     // xtype created footer. - not sure if will work as we normally have to render first..
46836     if (this.footer && !this.footer.el && this.footer.xtype) {
46837         
46838         this.footer.container = this.grid.getView().getFooterPanel(true);
46839         this.footer.dataSource = this.grid.dataSource;
46840         this.footer = Roo.factory(this.footer, Roo);
46841         
46842     }
46843     
46844     grid.monitorWindowResize = false; // turn off autosizing
46845     grid.autoHeight = false;
46846     grid.autoWidth = false;
46847     this.grid = grid;
46848     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46849 };
46850
46851 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46852     getId : function(){
46853         return this.grid.id;
46854     },
46855     
46856     /**
46857      * Returns the grid for this panel
46858      * @return {Roo.grid.Grid} 
46859      */
46860     getGrid : function(){
46861         return this.grid;    
46862     },
46863     
46864     setSize : function(width, height){
46865         if(!this.ignoreResize(width, height)){
46866             var grid = this.grid;
46867             var size = this.adjustForComponents(width, height);
46868             grid.getGridEl().setSize(size.width, size.height);
46869             grid.autoSize();
46870         }
46871     },
46872     
46873     beforeSlide : function(){
46874         this.grid.getView().scroller.clip();
46875     },
46876     
46877     afterSlide : function(){
46878         this.grid.getView().scroller.unclip();
46879     },
46880     
46881     destroy : function(){
46882         this.grid.destroy();
46883         delete this.grid;
46884         Roo.GridPanel.superclass.destroy.call(this); 
46885     }
46886 });
46887
46888
46889 /**
46890  * @class Roo.NestedLayoutPanel
46891  * @extends Roo.ContentPanel
46892  * @constructor
46893  * Create a new NestedLayoutPanel.
46894  * 
46895  * 
46896  * @param {Roo.BorderLayout} layout The layout for this panel
46897  * @param {String/Object} config A string to set only the title or a config object
46898  */
46899 Roo.NestedLayoutPanel = function(layout, config)
46900 {
46901     // construct with only one argument..
46902     /* FIXME - implement nicer consturctors
46903     if (layout.layout) {
46904         config = layout;
46905         layout = config.layout;
46906         delete config.layout;
46907     }
46908     if (layout.xtype && !layout.getEl) {
46909         // then layout needs constructing..
46910         layout = Roo.factory(layout, Roo);
46911     }
46912     */
46913     
46914     
46915     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46916     
46917     layout.monitorWindowResize = false; // turn off autosizing
46918     this.layout = layout;
46919     this.layout.getEl().addClass("x-layout-nested-layout");
46920     
46921     
46922     
46923     
46924 };
46925
46926 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46927
46928     setSize : function(width, height){
46929         if(!this.ignoreResize(width, height)){
46930             var size = this.adjustForComponents(width, height);
46931             var el = this.layout.getEl();
46932             el.setSize(size.width, size.height);
46933             var touch = el.dom.offsetWidth;
46934             this.layout.layout();
46935             // ie requires a double layout on the first pass
46936             if(Roo.isIE && !this.initialized){
46937                 this.initialized = true;
46938                 this.layout.layout();
46939             }
46940         }
46941     },
46942     
46943     // activate all subpanels if not currently active..
46944     
46945     setActiveState : function(active){
46946         this.active = active;
46947         if(!active){
46948             this.fireEvent("deactivate", this);
46949             return;
46950         }
46951         
46952         this.fireEvent("activate", this);
46953         // not sure if this should happen before or after..
46954         if (!this.layout) {
46955             return; // should not happen..
46956         }
46957         var reg = false;
46958         for (var r in this.layout.regions) {
46959             reg = this.layout.getRegion(r);
46960             if (reg.getActivePanel()) {
46961                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46962                 reg.setActivePanel(reg.getActivePanel());
46963                 continue;
46964             }
46965             if (!reg.panels.length) {
46966                 continue;
46967             }
46968             reg.showPanel(reg.getPanel(0));
46969         }
46970         
46971         
46972         
46973         
46974     },
46975     
46976     /**
46977      * Returns the nested BorderLayout for this panel
46978      * @return {Roo.BorderLayout} 
46979      */
46980     getLayout : function(){
46981         return this.layout;
46982     },
46983     
46984      /**
46985      * Adds a xtype elements to the layout of the nested panel
46986      * <pre><code>
46987
46988 panel.addxtype({
46989        xtype : 'ContentPanel',
46990        region: 'west',
46991        items: [ .... ]
46992    }
46993 );
46994
46995 panel.addxtype({
46996         xtype : 'NestedLayoutPanel',
46997         region: 'west',
46998         layout: {
46999            center: { },
47000            west: { }   
47001         },
47002         items : [ ... list of content panels or nested layout panels.. ]
47003    }
47004 );
47005 </code></pre>
47006      * @param {Object} cfg Xtype definition of item to add.
47007      */
47008     addxtype : function(cfg) {
47009         return this.layout.addxtype(cfg);
47010     
47011     }
47012 });
47013
47014 Roo.ScrollPanel = function(el, config, content){
47015     config = config || {};
47016     config.fitToFrame = true;
47017     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47018     
47019     this.el.dom.style.overflow = "hidden";
47020     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47021     this.el.removeClass("x-layout-inactive-content");
47022     this.el.on("mousewheel", this.onWheel, this);
47023
47024     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47025     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47026     up.unselectable(); down.unselectable();
47027     up.on("click", this.scrollUp, this);
47028     down.on("click", this.scrollDown, this);
47029     up.addClassOnOver("x-scroller-btn-over");
47030     down.addClassOnOver("x-scroller-btn-over");
47031     up.addClassOnClick("x-scroller-btn-click");
47032     down.addClassOnClick("x-scroller-btn-click");
47033     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47034
47035     this.resizeEl = this.el;
47036     this.el = wrap; this.up = up; this.down = down;
47037 };
47038
47039 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47040     increment : 100,
47041     wheelIncrement : 5,
47042     scrollUp : function(){
47043         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47044     },
47045
47046     scrollDown : function(){
47047         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47048     },
47049
47050     afterScroll : function(){
47051         var el = this.resizeEl;
47052         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47053         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47054         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47055     },
47056
47057     setSize : function(){
47058         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47059         this.afterScroll();
47060     },
47061
47062     onWheel : function(e){
47063         var d = e.getWheelDelta();
47064         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47065         this.afterScroll();
47066         e.stopEvent();
47067     },
47068
47069     setContent : function(content, loadScripts){
47070         this.resizeEl.update(content, loadScripts);
47071     }
47072
47073 });
47074
47075
47076
47077
47078
47079
47080
47081
47082
47083 /**
47084  * @class Roo.TreePanel
47085  * @extends Roo.ContentPanel
47086  * @constructor
47087  * Create a new TreePanel. - defaults to fit/scoll contents.
47088  * @param {String/Object} config A string to set only the panel's title, or a config object
47089  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47090  */
47091 Roo.TreePanel = function(config){
47092     var el = config.el;
47093     var tree = config.tree;
47094     delete config.tree; 
47095     delete config.el; // hopefull!
47096     
47097     // wrapper for IE7 strict & safari scroll issue
47098     
47099     var treeEl = el.createChild();
47100     config.resizeEl = treeEl;
47101     
47102     
47103     
47104     Roo.TreePanel.superclass.constructor.call(this, el, config);
47105  
47106  
47107     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47108     //console.log(tree);
47109     this.on('activate', function()
47110     {
47111         if (this.tree.rendered) {
47112             return;
47113         }
47114         //console.log('render tree');
47115         this.tree.render();
47116     });
47117     
47118     this.on('resize',  function (cp, w, h) {
47119             this.tree.innerCt.setWidth(w);
47120             this.tree.innerCt.setHeight(h);
47121             this.tree.innerCt.setStyle('overflow-y', 'auto');
47122     });
47123
47124         
47125     
47126 };
47127
47128 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47129     fitToFrame : true,
47130     autoScroll : true
47131 });
47132
47133
47134
47135
47136
47137
47138
47139
47140
47141
47142
47143 /*
47144  * Based on:
47145  * Ext JS Library 1.1.1
47146  * Copyright(c) 2006-2007, Ext JS, LLC.
47147  *
47148  * Originally Released Under LGPL - original licence link has changed is not relivant.
47149  *
47150  * Fork - LGPL
47151  * <script type="text/javascript">
47152  */
47153  
47154
47155 /**
47156  * @class Roo.ReaderLayout
47157  * @extends Roo.BorderLayout
47158  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47159  * center region containing two nested regions (a top one for a list view and one for item preview below),
47160  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47161  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47162  * expedites the setup of the overall layout and regions for this common application style.
47163  * Example:
47164  <pre><code>
47165 var reader = new Roo.ReaderLayout();
47166 var CP = Roo.ContentPanel;  // shortcut for adding
47167
47168 reader.beginUpdate();
47169 reader.add("north", new CP("north", "North"));
47170 reader.add("west", new CP("west", {title: "West"}));
47171 reader.add("east", new CP("east", {title: "East"}));
47172
47173 reader.regions.listView.add(new CP("listView", "List"));
47174 reader.regions.preview.add(new CP("preview", "Preview"));
47175 reader.endUpdate();
47176 </code></pre>
47177 * @constructor
47178 * Create a new ReaderLayout
47179 * @param {Object} config Configuration options
47180 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47181 * document.body if omitted)
47182 */
47183 Roo.ReaderLayout = function(config, renderTo){
47184     var c = config || {size:{}};
47185     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47186         north: c.north !== false ? Roo.apply({
47187             split:false,
47188             initialSize: 32,
47189             titlebar: false
47190         }, c.north) : false,
47191         west: c.west !== false ? Roo.apply({
47192             split:true,
47193             initialSize: 200,
47194             minSize: 175,
47195             maxSize: 400,
47196             titlebar: true,
47197             collapsible: true,
47198             animate: true,
47199             margins:{left:5,right:0,bottom:5,top:5},
47200             cmargins:{left:5,right:5,bottom:5,top:5}
47201         }, c.west) : false,
47202         east: c.east !== false ? Roo.apply({
47203             split:true,
47204             initialSize: 200,
47205             minSize: 175,
47206             maxSize: 400,
47207             titlebar: true,
47208             collapsible: true,
47209             animate: true,
47210             margins:{left:0,right:5,bottom:5,top:5},
47211             cmargins:{left:5,right:5,bottom:5,top:5}
47212         }, c.east) : false,
47213         center: Roo.apply({
47214             tabPosition: 'top',
47215             autoScroll:false,
47216             closeOnTab: true,
47217             titlebar:false,
47218             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47219         }, c.center)
47220     });
47221
47222     this.el.addClass('x-reader');
47223
47224     this.beginUpdate();
47225
47226     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47227         south: c.preview !== false ? Roo.apply({
47228             split:true,
47229             initialSize: 200,
47230             minSize: 100,
47231             autoScroll:true,
47232             collapsible:true,
47233             titlebar: true,
47234             cmargins:{top:5,left:0, right:0, bottom:0}
47235         }, c.preview) : false,
47236         center: Roo.apply({
47237             autoScroll:false,
47238             titlebar:false,
47239             minHeight:200
47240         }, c.listView)
47241     });
47242     this.add('center', new Roo.NestedLayoutPanel(inner,
47243             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47244
47245     this.endUpdate();
47246
47247     this.regions.preview = inner.getRegion('south');
47248     this.regions.listView = inner.getRegion('center');
47249 };
47250
47251 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47252  * Based on:
47253  * Ext JS Library 1.1.1
47254  * Copyright(c) 2006-2007, Ext JS, LLC.
47255  *
47256  * Originally Released Under LGPL - original licence link has changed is not relivant.
47257  *
47258  * Fork - LGPL
47259  * <script type="text/javascript">
47260  */
47261  
47262 /**
47263  * @class Roo.grid.Grid
47264  * @extends Roo.util.Observable
47265  * This class represents the primary interface of a component based grid control.
47266  * <br><br>Usage:<pre><code>
47267  var grid = new Roo.grid.Grid("my-container-id", {
47268      ds: myDataStore,
47269      cm: myColModel,
47270      selModel: mySelectionModel,
47271      autoSizeColumns: true,
47272      monitorWindowResize: false,
47273      trackMouseOver: true
47274  });
47275  // set any options
47276  grid.render();
47277  * </code></pre>
47278  * <b>Common Problems:</b><br/>
47279  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47280  * element will correct this<br/>
47281  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47282  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47283  * are unpredictable.<br/>
47284  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47285  * grid to calculate dimensions/offsets.<br/>
47286   * @constructor
47287  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47288  * The container MUST have some type of size defined for the grid to fill. The container will be
47289  * automatically set to position relative if it isn't already.
47290  * @param {Object} config A config object that sets properties on this grid.
47291  */
47292 Roo.grid.Grid = function(container, config){
47293         // initialize the container
47294         this.container = Roo.get(container);
47295         this.container.update("");
47296         this.container.setStyle("overflow", "hidden");
47297     this.container.addClass('x-grid-container');
47298
47299     this.id = this.container.id;
47300
47301     Roo.apply(this, config);
47302     // check and correct shorthanded configs
47303     if(this.ds){
47304         this.dataSource = this.ds;
47305         delete this.ds;
47306     }
47307     if(this.cm){
47308         this.colModel = this.cm;
47309         delete this.cm;
47310     }
47311     if(this.sm){
47312         this.selModel = this.sm;
47313         delete this.sm;
47314     }
47315
47316     if (this.selModel) {
47317         this.selModel = Roo.factory(this.selModel, Roo.grid);
47318         this.sm = this.selModel;
47319         this.sm.xmodule = this.xmodule || false;
47320     }
47321     if (typeof(this.colModel.config) == 'undefined') {
47322         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47323         this.cm = this.colModel;
47324         this.cm.xmodule = this.xmodule || false;
47325     }
47326     if (this.dataSource) {
47327         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47328         this.ds = this.dataSource;
47329         this.ds.xmodule = this.xmodule || false;
47330          
47331     }
47332     
47333     
47334     
47335     if(this.width){
47336         this.container.setWidth(this.width);
47337     }
47338
47339     if(this.height){
47340         this.container.setHeight(this.height);
47341     }
47342     /** @private */
47343         this.addEvents({
47344         // raw events
47345         /**
47346          * @event click
47347          * The raw click event for the entire grid.
47348          * @param {Roo.EventObject} e
47349          */
47350         "click" : true,
47351         /**
47352          * @event dblclick
47353          * The raw dblclick event for the entire grid.
47354          * @param {Roo.EventObject} e
47355          */
47356         "dblclick" : true,
47357         /**
47358          * @event contextmenu
47359          * The raw contextmenu event for the entire grid.
47360          * @param {Roo.EventObject} e
47361          */
47362         "contextmenu" : true,
47363         /**
47364          * @event mousedown
47365          * The raw mousedown event for the entire grid.
47366          * @param {Roo.EventObject} e
47367          */
47368         "mousedown" : true,
47369         /**
47370          * @event mouseup
47371          * The raw mouseup event for the entire grid.
47372          * @param {Roo.EventObject} e
47373          */
47374         "mouseup" : true,
47375         /**
47376          * @event mouseover
47377          * The raw mouseover event for the entire grid.
47378          * @param {Roo.EventObject} e
47379          */
47380         "mouseover" : true,
47381         /**
47382          * @event mouseout
47383          * The raw mouseout event for the entire grid.
47384          * @param {Roo.EventObject} e
47385          */
47386         "mouseout" : true,
47387         /**
47388          * @event keypress
47389          * The raw keypress event for the entire grid.
47390          * @param {Roo.EventObject} e
47391          */
47392         "keypress" : true,
47393         /**
47394          * @event keydown
47395          * The raw keydown event for the entire grid.
47396          * @param {Roo.EventObject} e
47397          */
47398         "keydown" : true,
47399
47400         // custom events
47401
47402         /**
47403          * @event cellclick
47404          * Fires when a cell is clicked
47405          * @param {Grid} this
47406          * @param {Number} rowIndex
47407          * @param {Number} columnIndex
47408          * @param {Roo.EventObject} e
47409          */
47410         "cellclick" : true,
47411         /**
47412          * @event celldblclick
47413          * Fires when a cell is double clicked
47414          * @param {Grid} this
47415          * @param {Number} rowIndex
47416          * @param {Number} columnIndex
47417          * @param {Roo.EventObject} e
47418          */
47419         "celldblclick" : true,
47420         /**
47421          * @event rowclick
47422          * Fires when a row is clicked
47423          * @param {Grid} this
47424          * @param {Number} rowIndex
47425          * @param {Roo.EventObject} e
47426          */
47427         "rowclick" : true,
47428         /**
47429          * @event rowdblclick
47430          * Fires when a row is double clicked
47431          * @param {Grid} this
47432          * @param {Number} rowIndex
47433          * @param {Roo.EventObject} e
47434          */
47435         "rowdblclick" : true,
47436         /**
47437          * @event headerclick
47438          * Fires when a header is clicked
47439          * @param {Grid} this
47440          * @param {Number} columnIndex
47441          * @param {Roo.EventObject} e
47442          */
47443         "headerclick" : true,
47444         /**
47445          * @event headerdblclick
47446          * Fires when a header cell is double clicked
47447          * @param {Grid} this
47448          * @param {Number} columnIndex
47449          * @param {Roo.EventObject} e
47450          */
47451         "headerdblclick" : true,
47452         /**
47453          * @event rowcontextmenu
47454          * Fires when a row is right clicked
47455          * @param {Grid} this
47456          * @param {Number} rowIndex
47457          * @param {Roo.EventObject} e
47458          */
47459         "rowcontextmenu" : true,
47460         /**
47461          * @event cellcontextmenu
47462          * Fires when a cell is right clicked
47463          * @param {Grid} this
47464          * @param {Number} rowIndex
47465          * @param {Number} cellIndex
47466          * @param {Roo.EventObject} e
47467          */
47468          "cellcontextmenu" : true,
47469         /**
47470          * @event headercontextmenu
47471          * Fires when a header is right clicked
47472          * @param {Grid} this
47473          * @param {Number} columnIndex
47474          * @param {Roo.EventObject} e
47475          */
47476         "headercontextmenu" : true,
47477         /**
47478          * @event bodyscroll
47479          * Fires when the body element is scrolled
47480          * @param {Number} scrollLeft
47481          * @param {Number} scrollTop
47482          */
47483         "bodyscroll" : true,
47484         /**
47485          * @event columnresize
47486          * Fires when the user resizes a column
47487          * @param {Number} columnIndex
47488          * @param {Number} newSize
47489          */
47490         "columnresize" : true,
47491         /**
47492          * @event columnmove
47493          * Fires when the user moves a column
47494          * @param {Number} oldIndex
47495          * @param {Number} newIndex
47496          */
47497         "columnmove" : true,
47498         /**
47499          * @event startdrag
47500          * Fires when row(s) start being dragged
47501          * @param {Grid} this
47502          * @param {Roo.GridDD} dd The drag drop object
47503          * @param {event} e The raw browser event
47504          */
47505         "startdrag" : true,
47506         /**
47507          * @event enddrag
47508          * Fires when a drag operation is complete
47509          * @param {Grid} this
47510          * @param {Roo.GridDD} dd The drag drop object
47511          * @param {event} e The raw browser event
47512          */
47513         "enddrag" : true,
47514         /**
47515          * @event dragdrop
47516          * Fires when dragged row(s) are dropped on a valid DD target
47517          * @param {Grid} this
47518          * @param {Roo.GridDD} dd The drag drop object
47519          * @param {String} targetId The target drag drop object
47520          * @param {event} e The raw browser event
47521          */
47522         "dragdrop" : true,
47523         /**
47524          * @event dragover
47525          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47526          * @param {Grid} this
47527          * @param {Roo.GridDD} dd The drag drop object
47528          * @param {String} targetId The target drag drop object
47529          * @param {event} e The raw browser event
47530          */
47531         "dragover" : true,
47532         /**
47533          * @event dragenter
47534          *  Fires when the dragged row(s) first cross another DD target while being dragged
47535          * @param {Grid} this
47536          * @param {Roo.GridDD} dd The drag drop object
47537          * @param {String} targetId The target drag drop object
47538          * @param {event} e The raw browser event
47539          */
47540         "dragenter" : true,
47541         /**
47542          * @event dragout
47543          * Fires when the dragged row(s) leave another DD target while being dragged
47544          * @param {Grid} this
47545          * @param {Roo.GridDD} dd The drag drop object
47546          * @param {String} targetId The target drag drop object
47547          * @param {event} e The raw browser event
47548          */
47549         "dragout" : true,
47550         /**
47551          * @event rowclass
47552          * Fires when a row is rendered, so you can change add a style to it.
47553          * @param {GridView} gridview   The grid view
47554          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47555          */
47556         'rowclass' : true,
47557
47558         /**
47559          * @event render
47560          * Fires when the grid is rendered
47561          * @param {Grid} grid
47562          */
47563         'render' : true
47564     });
47565
47566     Roo.grid.Grid.superclass.constructor.call(this);
47567 };
47568 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47569     
47570     /**
47571      * @cfg {String} ddGroup - drag drop group.
47572      */
47573
47574     /**
47575      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47576      */
47577     minColumnWidth : 25,
47578
47579     /**
47580      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47581      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47582      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47583      */
47584     autoSizeColumns : false,
47585
47586     /**
47587      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47588      */
47589     autoSizeHeaders : true,
47590
47591     /**
47592      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47593      */
47594     monitorWindowResize : true,
47595
47596     /**
47597      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47598      * rows measured to get a columns size. Default is 0 (all rows).
47599      */
47600     maxRowsToMeasure : 0,
47601
47602     /**
47603      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47604      */
47605     trackMouseOver : true,
47606
47607     /**
47608     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47609     */
47610     
47611     /**
47612     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47613     */
47614     enableDragDrop : false,
47615     
47616     /**
47617     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47618     */
47619     enableColumnMove : true,
47620     
47621     /**
47622     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47623     */
47624     enableColumnHide : true,
47625     
47626     /**
47627     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47628     */
47629     enableRowHeightSync : false,
47630     
47631     /**
47632     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47633     */
47634     stripeRows : true,
47635     
47636     /**
47637     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47638     */
47639     autoHeight : false,
47640
47641     /**
47642      * @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.
47643      */
47644     autoExpandColumn : false,
47645
47646     /**
47647     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47648     * Default is 50.
47649     */
47650     autoExpandMin : 50,
47651
47652     /**
47653     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47654     */
47655     autoExpandMax : 1000,
47656
47657     /**
47658     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47659     */
47660     view : null,
47661
47662     /**
47663     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47664     */
47665     loadMask : false,
47666     /**
47667     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47668     */
47669     dropTarget: false,
47670     
47671    
47672     
47673     // private
47674     rendered : false,
47675
47676     /**
47677     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47678     * of a fixed width. Default is false.
47679     */
47680     /**
47681     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47682     */
47683     /**
47684      * Called once after all setup has been completed and the grid is ready to be rendered.
47685      * @return {Roo.grid.Grid} this
47686      */
47687     render : function()
47688     {
47689         var c = this.container;
47690         // try to detect autoHeight/width mode
47691         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47692             this.autoHeight = true;
47693         }
47694         var view = this.getView();
47695         view.init(this);
47696
47697         c.on("click", this.onClick, this);
47698         c.on("dblclick", this.onDblClick, this);
47699         c.on("contextmenu", this.onContextMenu, this);
47700         c.on("keydown", this.onKeyDown, this);
47701
47702         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47703
47704         this.getSelectionModel().init(this);
47705
47706         view.render();
47707
47708         if(this.loadMask){
47709             this.loadMask = new Roo.LoadMask(this.container,
47710                     Roo.apply({store:this.dataSource}, this.loadMask));
47711         }
47712         
47713         
47714         if (this.toolbar && this.toolbar.xtype) {
47715             this.toolbar.container = this.getView().getHeaderPanel(true);
47716             this.toolbar = new Roo.Toolbar(this.toolbar);
47717         }
47718         if (this.footer && this.footer.xtype) {
47719             this.footer.dataSource = this.getDataSource();
47720             this.footer.container = this.getView().getFooterPanel(true);
47721             this.footer = Roo.factory(this.footer, Roo);
47722         }
47723         if (this.dropTarget && this.dropTarget.xtype) {
47724             delete this.dropTarget.xtype;
47725             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47726         }
47727         
47728         
47729         this.rendered = true;
47730         this.fireEvent('render', this);
47731         return this;
47732     },
47733
47734         /**
47735          * Reconfigures the grid to use a different Store and Column Model.
47736          * The View will be bound to the new objects and refreshed.
47737          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47738          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47739          */
47740     reconfigure : function(dataSource, colModel){
47741         if(this.loadMask){
47742             this.loadMask.destroy();
47743             this.loadMask = new Roo.LoadMask(this.container,
47744                     Roo.apply({store:dataSource}, this.loadMask));
47745         }
47746         this.view.bind(dataSource, colModel);
47747         this.dataSource = dataSource;
47748         this.colModel = colModel;
47749         this.view.refresh(true);
47750     },
47751
47752     // private
47753     onKeyDown : function(e){
47754         this.fireEvent("keydown", e);
47755     },
47756
47757     /**
47758      * Destroy this grid.
47759      * @param {Boolean} removeEl True to remove the element
47760      */
47761     destroy : function(removeEl, keepListeners){
47762         if(this.loadMask){
47763             this.loadMask.destroy();
47764         }
47765         var c = this.container;
47766         c.removeAllListeners();
47767         this.view.destroy();
47768         this.colModel.purgeListeners();
47769         if(!keepListeners){
47770             this.purgeListeners();
47771         }
47772         c.update("");
47773         if(removeEl === true){
47774             c.remove();
47775         }
47776     },
47777
47778     // private
47779     processEvent : function(name, e){
47780         this.fireEvent(name, e);
47781         var t = e.getTarget();
47782         var v = this.view;
47783         var header = v.findHeaderIndex(t);
47784         if(header !== false){
47785             this.fireEvent("header" + name, this, header, e);
47786         }else{
47787             var row = v.findRowIndex(t);
47788             var cell = v.findCellIndex(t);
47789             if(row !== false){
47790                 this.fireEvent("row" + name, this, row, e);
47791                 if(cell !== false){
47792                     this.fireEvent("cell" + name, this, row, cell, e);
47793                 }
47794             }
47795         }
47796     },
47797
47798     // private
47799     onClick : function(e){
47800         this.processEvent("click", e);
47801     },
47802
47803     // private
47804     onContextMenu : function(e, t){
47805         this.processEvent("contextmenu", e);
47806     },
47807
47808     // private
47809     onDblClick : function(e){
47810         this.processEvent("dblclick", e);
47811     },
47812
47813     // private
47814     walkCells : function(row, col, step, fn, scope){
47815         var cm = this.colModel, clen = cm.getColumnCount();
47816         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47817         if(step < 0){
47818             if(col < 0){
47819                 row--;
47820                 first = false;
47821             }
47822             while(row >= 0){
47823                 if(!first){
47824                     col = clen-1;
47825                 }
47826                 first = false;
47827                 while(col >= 0){
47828                     if(fn.call(scope || this, row, col, cm) === true){
47829                         return [row, col];
47830                     }
47831                     col--;
47832                 }
47833                 row--;
47834             }
47835         } else {
47836             if(col >= clen){
47837                 row++;
47838                 first = false;
47839             }
47840             while(row < rlen){
47841                 if(!first){
47842                     col = 0;
47843                 }
47844                 first = false;
47845                 while(col < clen){
47846                     if(fn.call(scope || this, row, col, cm) === true){
47847                         return [row, col];
47848                     }
47849                     col++;
47850                 }
47851                 row++;
47852             }
47853         }
47854         return null;
47855     },
47856
47857     // private
47858     getSelections : function(){
47859         return this.selModel.getSelections();
47860     },
47861
47862     /**
47863      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47864      * but if manual update is required this method will initiate it.
47865      */
47866     autoSize : function(){
47867         if(this.rendered){
47868             this.view.layout();
47869             if(this.view.adjustForScroll){
47870                 this.view.adjustForScroll();
47871             }
47872         }
47873     },
47874
47875     /**
47876      * Returns the grid's underlying element.
47877      * @return {Element} The element
47878      */
47879     getGridEl : function(){
47880         return this.container;
47881     },
47882
47883     // private for compatibility, overridden by editor grid
47884     stopEditing : function(){},
47885
47886     /**
47887      * Returns the grid's SelectionModel.
47888      * @return {SelectionModel}
47889      */
47890     getSelectionModel : function(){
47891         if(!this.selModel){
47892             this.selModel = new Roo.grid.RowSelectionModel();
47893         }
47894         return this.selModel;
47895     },
47896
47897     /**
47898      * Returns the grid's DataSource.
47899      * @return {DataSource}
47900      */
47901     getDataSource : function(){
47902         return this.dataSource;
47903     },
47904
47905     /**
47906      * Returns the grid's ColumnModel.
47907      * @return {ColumnModel}
47908      */
47909     getColumnModel : function(){
47910         return this.colModel;
47911     },
47912
47913     /**
47914      * Returns the grid's GridView object.
47915      * @return {GridView}
47916      */
47917     getView : function(){
47918         if(!this.view){
47919             this.view = new Roo.grid.GridView(this.viewConfig);
47920         }
47921         return this.view;
47922     },
47923     /**
47924      * Called to get grid's drag proxy text, by default returns this.ddText.
47925      * @return {String}
47926      */
47927     getDragDropText : function(){
47928         var count = this.selModel.getCount();
47929         return String.format(this.ddText, count, count == 1 ? '' : 's');
47930     }
47931 });
47932 /**
47933  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47934  * %0 is replaced with the number of selected rows.
47935  * @type String
47936  */
47937 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47938  * Based on:
47939  * Ext JS Library 1.1.1
47940  * Copyright(c) 2006-2007, Ext JS, LLC.
47941  *
47942  * Originally Released Under LGPL - original licence link has changed is not relivant.
47943  *
47944  * Fork - LGPL
47945  * <script type="text/javascript">
47946  */
47947  
47948 Roo.grid.AbstractGridView = function(){
47949         this.grid = null;
47950         
47951         this.events = {
47952             "beforerowremoved" : true,
47953             "beforerowsinserted" : true,
47954             "beforerefresh" : true,
47955             "rowremoved" : true,
47956             "rowsinserted" : true,
47957             "rowupdated" : true,
47958             "refresh" : true
47959         };
47960     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47961 };
47962
47963 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47964     rowClass : "x-grid-row",
47965     cellClass : "x-grid-cell",
47966     tdClass : "x-grid-td",
47967     hdClass : "x-grid-hd",
47968     splitClass : "x-grid-hd-split",
47969     
47970         init: function(grid){
47971         this.grid = grid;
47972                 var cid = this.grid.getGridEl().id;
47973         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47974         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47975         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47976         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47977         },
47978         
47979         getColumnRenderers : function(){
47980         var renderers = [];
47981         var cm = this.grid.colModel;
47982         var colCount = cm.getColumnCount();
47983         for(var i = 0; i < colCount; i++){
47984             renderers[i] = cm.getRenderer(i);
47985         }
47986         return renderers;
47987     },
47988     
47989     getColumnIds : function(){
47990         var ids = [];
47991         var cm = this.grid.colModel;
47992         var colCount = cm.getColumnCount();
47993         for(var i = 0; i < colCount; i++){
47994             ids[i] = cm.getColumnId(i);
47995         }
47996         return ids;
47997     },
47998     
47999     getDataIndexes : function(){
48000         if(!this.indexMap){
48001             this.indexMap = this.buildIndexMap();
48002         }
48003         return this.indexMap.colToData;
48004     },
48005     
48006     getColumnIndexByDataIndex : function(dataIndex){
48007         if(!this.indexMap){
48008             this.indexMap = this.buildIndexMap();
48009         }
48010         return this.indexMap.dataToCol[dataIndex];
48011     },
48012     
48013     /**
48014      * Set a css style for a column dynamically. 
48015      * @param {Number} colIndex The index of the column
48016      * @param {String} name The css property name
48017      * @param {String} value The css value
48018      */
48019     setCSSStyle : function(colIndex, name, value){
48020         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48021         Roo.util.CSS.updateRule(selector, name, value);
48022     },
48023     
48024     generateRules : function(cm){
48025         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48026         Roo.util.CSS.removeStyleSheet(rulesId);
48027         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48028             var cid = cm.getColumnId(i);
48029             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48030                          this.tdSelector, cid, " {\n}\n",
48031                          this.hdSelector, cid, " {\n}\n",
48032                          this.splitSelector, cid, " {\n}\n");
48033         }
48034         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48035     }
48036 });/*
48037  * Based on:
48038  * Ext JS Library 1.1.1
48039  * Copyright(c) 2006-2007, Ext JS, LLC.
48040  *
48041  * Originally Released Under LGPL - original licence link has changed is not relivant.
48042  *
48043  * Fork - LGPL
48044  * <script type="text/javascript">
48045  */
48046
48047 // private
48048 // This is a support class used internally by the Grid components
48049 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48050     this.grid = grid;
48051     this.view = grid.getView();
48052     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48053     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48054     if(hd2){
48055         this.setHandleElId(Roo.id(hd));
48056         this.setOuterHandleElId(Roo.id(hd2));
48057     }
48058     this.scroll = false;
48059 };
48060 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48061     maxDragWidth: 120,
48062     getDragData : function(e){
48063         var t = Roo.lib.Event.getTarget(e);
48064         var h = this.view.findHeaderCell(t);
48065         if(h){
48066             return {ddel: h.firstChild, header:h};
48067         }
48068         return false;
48069     },
48070
48071     onInitDrag : function(e){
48072         this.view.headersDisabled = true;
48073         var clone = this.dragData.ddel.cloneNode(true);
48074         clone.id = Roo.id();
48075         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48076         this.proxy.update(clone);
48077         return true;
48078     },
48079
48080     afterValidDrop : function(){
48081         var v = this.view;
48082         setTimeout(function(){
48083             v.headersDisabled = false;
48084         }, 50);
48085     },
48086
48087     afterInvalidDrop : function(){
48088         var v = this.view;
48089         setTimeout(function(){
48090             v.headersDisabled = false;
48091         }, 50);
48092     }
48093 });
48094 /*
48095  * Based on:
48096  * Ext JS Library 1.1.1
48097  * Copyright(c) 2006-2007, Ext JS, LLC.
48098  *
48099  * Originally Released Under LGPL - original licence link has changed is not relivant.
48100  *
48101  * Fork - LGPL
48102  * <script type="text/javascript">
48103  */
48104 // private
48105 // This is a support class used internally by the Grid components
48106 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48107     this.grid = grid;
48108     this.view = grid.getView();
48109     // split the proxies so they don't interfere with mouse events
48110     this.proxyTop = Roo.DomHelper.append(document.body, {
48111         cls:"col-move-top", html:"&#160;"
48112     }, true);
48113     this.proxyBottom = Roo.DomHelper.append(document.body, {
48114         cls:"col-move-bottom", html:"&#160;"
48115     }, true);
48116     this.proxyTop.hide = this.proxyBottom.hide = function(){
48117         this.setLeftTop(-100,-100);
48118         this.setStyle("visibility", "hidden");
48119     };
48120     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48121     // temporarily disabled
48122     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48123     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48124 };
48125 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48126     proxyOffsets : [-4, -9],
48127     fly: Roo.Element.fly,
48128
48129     getTargetFromEvent : function(e){
48130         var t = Roo.lib.Event.getTarget(e);
48131         var cindex = this.view.findCellIndex(t);
48132         if(cindex !== false){
48133             return this.view.getHeaderCell(cindex);
48134         }
48135         return null;
48136     },
48137
48138     nextVisible : function(h){
48139         var v = this.view, cm = this.grid.colModel;
48140         h = h.nextSibling;
48141         while(h){
48142             if(!cm.isHidden(v.getCellIndex(h))){
48143                 return h;
48144             }
48145             h = h.nextSibling;
48146         }
48147         return null;
48148     },
48149
48150     prevVisible : function(h){
48151         var v = this.view, cm = this.grid.colModel;
48152         h = h.prevSibling;
48153         while(h){
48154             if(!cm.isHidden(v.getCellIndex(h))){
48155                 return h;
48156             }
48157             h = h.prevSibling;
48158         }
48159         return null;
48160     },
48161
48162     positionIndicator : function(h, n, e){
48163         var x = Roo.lib.Event.getPageX(e);
48164         var r = Roo.lib.Dom.getRegion(n.firstChild);
48165         var px, pt, py = r.top + this.proxyOffsets[1];
48166         if((r.right - x) <= (r.right-r.left)/2){
48167             px = r.right+this.view.borderWidth;
48168             pt = "after";
48169         }else{
48170             px = r.left;
48171             pt = "before";
48172         }
48173         var oldIndex = this.view.getCellIndex(h);
48174         var newIndex = this.view.getCellIndex(n);
48175
48176         if(this.grid.colModel.isFixed(newIndex)){
48177             return false;
48178         }
48179
48180         var locked = this.grid.colModel.isLocked(newIndex);
48181
48182         if(pt == "after"){
48183             newIndex++;
48184         }
48185         if(oldIndex < newIndex){
48186             newIndex--;
48187         }
48188         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48189             return false;
48190         }
48191         px +=  this.proxyOffsets[0];
48192         this.proxyTop.setLeftTop(px, py);
48193         this.proxyTop.show();
48194         if(!this.bottomOffset){
48195             this.bottomOffset = this.view.mainHd.getHeight();
48196         }
48197         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48198         this.proxyBottom.show();
48199         return pt;
48200     },
48201
48202     onNodeEnter : function(n, dd, e, data){
48203         if(data.header != n){
48204             this.positionIndicator(data.header, n, e);
48205         }
48206     },
48207
48208     onNodeOver : function(n, dd, e, data){
48209         var result = false;
48210         if(data.header != n){
48211             result = this.positionIndicator(data.header, n, e);
48212         }
48213         if(!result){
48214             this.proxyTop.hide();
48215             this.proxyBottom.hide();
48216         }
48217         return result ? this.dropAllowed : this.dropNotAllowed;
48218     },
48219
48220     onNodeOut : function(n, dd, e, data){
48221         this.proxyTop.hide();
48222         this.proxyBottom.hide();
48223     },
48224
48225     onNodeDrop : function(n, dd, e, data){
48226         var h = data.header;
48227         if(h != n){
48228             var cm = this.grid.colModel;
48229             var x = Roo.lib.Event.getPageX(e);
48230             var r = Roo.lib.Dom.getRegion(n.firstChild);
48231             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48232             var oldIndex = this.view.getCellIndex(h);
48233             var newIndex = this.view.getCellIndex(n);
48234             var locked = cm.isLocked(newIndex);
48235             if(pt == "after"){
48236                 newIndex++;
48237             }
48238             if(oldIndex < newIndex){
48239                 newIndex--;
48240             }
48241             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48242                 return false;
48243             }
48244             cm.setLocked(oldIndex, locked, true);
48245             cm.moveColumn(oldIndex, newIndex);
48246             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48247             return true;
48248         }
48249         return false;
48250     }
48251 });
48252 /*
48253  * Based on:
48254  * Ext JS Library 1.1.1
48255  * Copyright(c) 2006-2007, Ext JS, LLC.
48256  *
48257  * Originally Released Under LGPL - original licence link has changed is not relivant.
48258  *
48259  * Fork - LGPL
48260  * <script type="text/javascript">
48261  */
48262   
48263 /**
48264  * @class Roo.grid.GridView
48265  * @extends Roo.util.Observable
48266  *
48267  * @constructor
48268  * @param {Object} config
48269  */
48270 Roo.grid.GridView = function(config){
48271     Roo.grid.GridView.superclass.constructor.call(this);
48272     this.el = null;
48273
48274     Roo.apply(this, config);
48275 };
48276
48277 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48278
48279     /**
48280      * Override this function to apply custom css classes to rows during rendering
48281      * @param {Record} record The record
48282      * @param {Number} index
48283      * @method getRowClass
48284      */
48285     rowClass : "x-grid-row",
48286
48287     cellClass : "x-grid-col",
48288
48289     tdClass : "x-grid-td",
48290
48291     hdClass : "x-grid-hd",
48292
48293     splitClass : "x-grid-split",
48294
48295     sortClasses : ["sort-asc", "sort-desc"],
48296
48297     enableMoveAnim : false,
48298
48299     hlColor: "C3DAF9",
48300
48301     dh : Roo.DomHelper,
48302
48303     fly : Roo.Element.fly,
48304
48305     css : Roo.util.CSS,
48306
48307     borderWidth: 1,
48308
48309     splitOffset: 3,
48310
48311     scrollIncrement : 22,
48312
48313     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48314
48315     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48316
48317     bind : function(ds, cm){
48318         if(this.ds){
48319             this.ds.un("load", this.onLoad, this);
48320             this.ds.un("datachanged", this.onDataChange, this);
48321             this.ds.un("add", this.onAdd, this);
48322             this.ds.un("remove", this.onRemove, this);
48323             this.ds.un("update", this.onUpdate, this);
48324             this.ds.un("clear", this.onClear, this);
48325         }
48326         if(ds){
48327             ds.on("load", this.onLoad, this);
48328             ds.on("datachanged", this.onDataChange, this);
48329             ds.on("add", this.onAdd, this);
48330             ds.on("remove", this.onRemove, this);
48331             ds.on("update", this.onUpdate, this);
48332             ds.on("clear", this.onClear, this);
48333         }
48334         this.ds = ds;
48335
48336         if(this.cm){
48337             this.cm.un("widthchange", this.onColWidthChange, this);
48338             this.cm.un("headerchange", this.onHeaderChange, this);
48339             this.cm.un("hiddenchange", this.onHiddenChange, this);
48340             this.cm.un("columnmoved", this.onColumnMove, this);
48341             this.cm.un("columnlockchange", this.onColumnLock, this);
48342         }
48343         if(cm){
48344             this.generateRules(cm);
48345             cm.on("widthchange", this.onColWidthChange, this);
48346             cm.on("headerchange", this.onHeaderChange, this);
48347             cm.on("hiddenchange", this.onHiddenChange, this);
48348             cm.on("columnmoved", this.onColumnMove, this);
48349             cm.on("columnlockchange", this.onColumnLock, this);
48350         }
48351         this.cm = cm;
48352     },
48353
48354     init: function(grid){
48355         Roo.grid.GridView.superclass.init.call(this, grid);
48356
48357         this.bind(grid.dataSource, grid.colModel);
48358
48359         grid.on("headerclick", this.handleHeaderClick, this);
48360
48361         if(grid.trackMouseOver){
48362             grid.on("mouseover", this.onRowOver, this);
48363             grid.on("mouseout", this.onRowOut, this);
48364         }
48365         grid.cancelTextSelection = function(){};
48366         this.gridId = grid.id;
48367
48368         var tpls = this.templates || {};
48369
48370         if(!tpls.master){
48371             tpls.master = new Roo.Template(
48372                '<div class="x-grid" hidefocus="true">',
48373                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48374                   '<div class="x-grid-topbar"></div>',
48375                   '<div class="x-grid-scroller"><div></div></div>',
48376                   '<div class="x-grid-locked">',
48377                       '<div class="x-grid-header">{lockedHeader}</div>',
48378                       '<div class="x-grid-body">{lockedBody}</div>',
48379                   "</div>",
48380                   '<div class="x-grid-viewport">',
48381                       '<div class="x-grid-header">{header}</div>',
48382                       '<div class="x-grid-body">{body}</div>',
48383                   "</div>",
48384                   '<div class="x-grid-bottombar"></div>',
48385                  
48386                   '<div class="x-grid-resize-proxy">&#160;</div>',
48387                "</div>"
48388             );
48389             tpls.master.disableformats = true;
48390         }
48391
48392         if(!tpls.header){
48393             tpls.header = new Roo.Template(
48394                '<table border="0" cellspacing="0" cellpadding="0">',
48395                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48396                "</table>{splits}"
48397             );
48398             tpls.header.disableformats = true;
48399         }
48400         tpls.header.compile();
48401
48402         if(!tpls.hcell){
48403             tpls.hcell = new Roo.Template(
48404                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48405                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48406                 "</div></td>"
48407              );
48408              tpls.hcell.disableFormats = true;
48409         }
48410         tpls.hcell.compile();
48411
48412         if(!tpls.hsplit){
48413             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48414             tpls.hsplit.disableFormats = true;
48415         }
48416         tpls.hsplit.compile();
48417
48418         if(!tpls.body){
48419             tpls.body = new Roo.Template(
48420                '<table border="0" cellspacing="0" cellpadding="0">',
48421                "<tbody>{rows}</tbody>",
48422                "</table>"
48423             );
48424             tpls.body.disableFormats = true;
48425         }
48426         tpls.body.compile();
48427
48428         if(!tpls.row){
48429             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48430             tpls.row.disableFormats = true;
48431         }
48432         tpls.row.compile();
48433
48434         if(!tpls.cell){
48435             tpls.cell = new Roo.Template(
48436                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48437                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48438                 "</td>"
48439             );
48440             tpls.cell.disableFormats = true;
48441         }
48442         tpls.cell.compile();
48443
48444         this.templates = tpls;
48445     },
48446
48447     // remap these for backwards compat
48448     onColWidthChange : function(){
48449         this.updateColumns.apply(this, arguments);
48450     },
48451     onHeaderChange : function(){
48452         this.updateHeaders.apply(this, arguments);
48453     }, 
48454     onHiddenChange : function(){
48455         this.handleHiddenChange.apply(this, arguments);
48456     },
48457     onColumnMove : function(){
48458         this.handleColumnMove.apply(this, arguments);
48459     },
48460     onColumnLock : function(){
48461         this.handleLockChange.apply(this, arguments);
48462     },
48463
48464     onDataChange : function(){
48465         this.refresh();
48466         this.updateHeaderSortState();
48467     },
48468
48469     onClear : function(){
48470         this.refresh();
48471     },
48472
48473     onUpdate : function(ds, record){
48474         this.refreshRow(record);
48475     },
48476
48477     refreshRow : function(record){
48478         var ds = this.ds, index;
48479         if(typeof record == 'number'){
48480             index = record;
48481             record = ds.getAt(index);
48482         }else{
48483             index = ds.indexOf(record);
48484         }
48485         this.insertRows(ds, index, index, true);
48486         this.onRemove(ds, record, index+1, true);
48487         this.syncRowHeights(index, index);
48488         this.layout();
48489         this.fireEvent("rowupdated", this, index, record);
48490     },
48491
48492     onAdd : function(ds, records, index){
48493         this.insertRows(ds, index, index + (records.length-1));
48494     },
48495
48496     onRemove : function(ds, record, index, isUpdate){
48497         if(isUpdate !== true){
48498             this.fireEvent("beforerowremoved", this, index, record);
48499         }
48500         var bt = this.getBodyTable(), lt = this.getLockedTable();
48501         if(bt.rows[index]){
48502             bt.firstChild.removeChild(bt.rows[index]);
48503         }
48504         if(lt.rows[index]){
48505             lt.firstChild.removeChild(lt.rows[index]);
48506         }
48507         if(isUpdate !== true){
48508             this.stripeRows(index);
48509             this.syncRowHeights(index, index);
48510             this.layout();
48511             this.fireEvent("rowremoved", this, index, record);
48512         }
48513     },
48514
48515     onLoad : function(){
48516         this.scrollToTop();
48517     },
48518
48519     /**
48520      * Scrolls the grid to the top
48521      */
48522     scrollToTop : function(){
48523         if(this.scroller){
48524             this.scroller.dom.scrollTop = 0;
48525             this.syncScroll();
48526         }
48527     },
48528
48529     /**
48530      * Gets a panel in the header of the grid that can be used for toolbars etc.
48531      * After modifying the contents of this panel a call to grid.autoSize() may be
48532      * required to register any changes in size.
48533      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48534      * @return Roo.Element
48535      */
48536     getHeaderPanel : function(doShow){
48537         if(doShow){
48538             this.headerPanel.show();
48539         }
48540         return this.headerPanel;
48541     },
48542
48543     /**
48544      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48545      * After modifying the contents of this panel a call to grid.autoSize() may be
48546      * required to register any changes in size.
48547      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48548      * @return Roo.Element
48549      */
48550     getFooterPanel : function(doShow){
48551         if(doShow){
48552             this.footerPanel.show();
48553         }
48554         return this.footerPanel;
48555     },
48556
48557     initElements : function(){
48558         var E = Roo.Element;
48559         var el = this.grid.getGridEl().dom.firstChild;
48560         var cs = el.childNodes;
48561
48562         this.el = new E(el);
48563         
48564          this.focusEl = new E(el.firstChild);
48565         this.focusEl.swallowEvent("click", true);
48566         
48567         this.headerPanel = new E(cs[1]);
48568         this.headerPanel.enableDisplayMode("block");
48569
48570         this.scroller = new E(cs[2]);
48571         this.scrollSizer = new E(this.scroller.dom.firstChild);
48572
48573         this.lockedWrap = new E(cs[3]);
48574         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48575         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48576
48577         this.mainWrap = new E(cs[4]);
48578         this.mainHd = new E(this.mainWrap.dom.firstChild);
48579         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48580
48581         this.footerPanel = new E(cs[5]);
48582         this.footerPanel.enableDisplayMode("block");
48583
48584         this.resizeProxy = new E(cs[6]);
48585
48586         this.headerSelector = String.format(
48587            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48588            this.lockedHd.id, this.mainHd.id
48589         );
48590
48591         this.splitterSelector = String.format(
48592            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48593            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48594         );
48595     },
48596     idToCssName : function(s)
48597     {
48598         return s.replace(/[^a-z0-9]+/ig, '-');
48599     },
48600
48601     getHeaderCell : function(index){
48602         return Roo.DomQuery.select(this.headerSelector)[index];
48603     },
48604
48605     getHeaderCellMeasure : function(index){
48606         return this.getHeaderCell(index).firstChild;
48607     },
48608
48609     getHeaderCellText : function(index){
48610         return this.getHeaderCell(index).firstChild.firstChild;
48611     },
48612
48613     getLockedTable : function(){
48614         return this.lockedBody.dom.firstChild;
48615     },
48616
48617     getBodyTable : function(){
48618         return this.mainBody.dom.firstChild;
48619     },
48620
48621     getLockedRow : function(index){
48622         return this.getLockedTable().rows[index];
48623     },
48624
48625     getRow : function(index){
48626         return this.getBodyTable().rows[index];
48627     },
48628
48629     getRowComposite : function(index){
48630         if(!this.rowEl){
48631             this.rowEl = new Roo.CompositeElementLite();
48632         }
48633         var els = [], lrow, mrow;
48634         if(lrow = this.getLockedRow(index)){
48635             els.push(lrow);
48636         }
48637         if(mrow = this.getRow(index)){
48638             els.push(mrow);
48639         }
48640         this.rowEl.elements = els;
48641         return this.rowEl;
48642     },
48643     /**
48644      * Gets the 'td' of the cell
48645      * 
48646      * @param {Integer} rowIndex row to select
48647      * @param {Integer} colIndex column to select
48648      * 
48649      * @return {Object} 
48650      */
48651     getCell : function(rowIndex, colIndex){
48652         var locked = this.cm.getLockedCount();
48653         var source;
48654         if(colIndex < locked){
48655             source = this.lockedBody.dom.firstChild;
48656         }else{
48657             source = this.mainBody.dom.firstChild;
48658             colIndex -= locked;
48659         }
48660         return source.rows[rowIndex].childNodes[colIndex];
48661     },
48662
48663     getCellText : function(rowIndex, colIndex){
48664         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48665     },
48666
48667     getCellBox : function(cell){
48668         var b = this.fly(cell).getBox();
48669         if(Roo.isOpera){ // opera fails to report the Y
48670             b.y = cell.offsetTop + this.mainBody.getY();
48671         }
48672         return b;
48673     },
48674
48675     getCellIndex : function(cell){
48676         var id = String(cell.className).match(this.cellRE);
48677         if(id){
48678             return parseInt(id[1], 10);
48679         }
48680         return 0;
48681     },
48682
48683     findHeaderIndex : function(n){
48684         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48685         return r ? this.getCellIndex(r) : false;
48686     },
48687
48688     findHeaderCell : function(n){
48689         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48690         return r ? r : false;
48691     },
48692
48693     findRowIndex : function(n){
48694         if(!n){
48695             return false;
48696         }
48697         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48698         return r ? r.rowIndex : false;
48699     },
48700
48701     findCellIndex : function(node){
48702         var stop = this.el.dom;
48703         while(node && node != stop){
48704             if(this.findRE.test(node.className)){
48705                 return this.getCellIndex(node);
48706             }
48707             node = node.parentNode;
48708         }
48709         return false;
48710     },
48711
48712     getColumnId : function(index){
48713         return this.cm.getColumnId(index);
48714     },
48715
48716     getSplitters : function()
48717     {
48718         if(this.splitterSelector){
48719            return Roo.DomQuery.select(this.splitterSelector);
48720         }else{
48721             return null;
48722       }
48723     },
48724
48725     getSplitter : function(index){
48726         return this.getSplitters()[index];
48727     },
48728
48729     onRowOver : function(e, t){
48730         var row;
48731         if((row = this.findRowIndex(t)) !== false){
48732             this.getRowComposite(row).addClass("x-grid-row-over");
48733         }
48734     },
48735
48736     onRowOut : function(e, t){
48737         var row;
48738         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48739             this.getRowComposite(row).removeClass("x-grid-row-over");
48740         }
48741     },
48742
48743     renderHeaders : function(){
48744         var cm = this.cm;
48745         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48746         var cb = [], lb = [], sb = [], lsb = [], p = {};
48747         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48748             p.cellId = "x-grid-hd-0-" + i;
48749             p.splitId = "x-grid-csplit-0-" + i;
48750             p.id = cm.getColumnId(i);
48751             p.title = cm.getColumnTooltip(i) || "";
48752             p.value = cm.getColumnHeader(i) || "";
48753             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48754             if(!cm.isLocked(i)){
48755                 cb[cb.length] = ct.apply(p);
48756                 sb[sb.length] = st.apply(p);
48757             }else{
48758                 lb[lb.length] = ct.apply(p);
48759                 lsb[lsb.length] = st.apply(p);
48760             }
48761         }
48762         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48763                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48764     },
48765
48766     updateHeaders : function(){
48767         var html = this.renderHeaders();
48768         this.lockedHd.update(html[0]);
48769         this.mainHd.update(html[1]);
48770     },
48771
48772     /**
48773      * Focuses the specified row.
48774      * @param {Number} row The row index
48775      */
48776     focusRow : function(row)
48777     {
48778         //Roo.log('GridView.focusRow');
48779         var x = this.scroller.dom.scrollLeft;
48780         this.focusCell(row, 0, false);
48781         this.scroller.dom.scrollLeft = x;
48782     },
48783
48784     /**
48785      * Focuses the specified cell.
48786      * @param {Number} row The row index
48787      * @param {Number} col The column index
48788      * @param {Boolean} hscroll false to disable horizontal scrolling
48789      */
48790     focusCell : function(row, col, hscroll)
48791     {
48792         //Roo.log('GridView.focusCell');
48793         var el = this.ensureVisible(row, col, hscroll);
48794         this.focusEl.alignTo(el, "tl-tl");
48795         if(Roo.isGecko){
48796             this.focusEl.focus();
48797         }else{
48798             this.focusEl.focus.defer(1, this.focusEl);
48799         }
48800     },
48801
48802     /**
48803      * Scrolls the specified cell into view
48804      * @param {Number} row The row index
48805      * @param {Number} col The column index
48806      * @param {Boolean} hscroll false to disable horizontal scrolling
48807      */
48808     ensureVisible : function(row, col, hscroll)
48809     {
48810         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48811         //return null; //disable for testing.
48812         if(typeof row != "number"){
48813             row = row.rowIndex;
48814         }
48815         if(row < 0 && row >= this.ds.getCount()){
48816             return  null;
48817         }
48818         col = (col !== undefined ? col : 0);
48819         var cm = this.grid.colModel;
48820         while(cm.isHidden(col)){
48821             col++;
48822         }
48823
48824         var el = this.getCell(row, col);
48825         if(!el){
48826             return null;
48827         }
48828         var c = this.scroller.dom;
48829
48830         var ctop = parseInt(el.offsetTop, 10);
48831         var cleft = parseInt(el.offsetLeft, 10);
48832         var cbot = ctop + el.offsetHeight;
48833         var cright = cleft + el.offsetWidth;
48834         
48835         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48836         var stop = parseInt(c.scrollTop, 10);
48837         var sleft = parseInt(c.scrollLeft, 10);
48838         var sbot = stop + ch;
48839         var sright = sleft + c.clientWidth;
48840         /*
48841         Roo.log('GridView.ensureVisible:' +
48842                 ' ctop:' + ctop +
48843                 ' c.clientHeight:' + c.clientHeight +
48844                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48845                 ' stop:' + stop +
48846                 ' cbot:' + cbot +
48847                 ' sbot:' + sbot +
48848                 ' ch:' + ch  
48849                 );
48850         */
48851         if(ctop < stop){
48852              c.scrollTop = ctop;
48853             //Roo.log("set scrolltop to ctop DISABLE?");
48854         }else if(cbot > sbot){
48855             //Roo.log("set scrolltop to cbot-ch");
48856             c.scrollTop = cbot-ch;
48857         }
48858         
48859         if(hscroll !== false){
48860             if(cleft < sleft){
48861                 c.scrollLeft = cleft;
48862             }else if(cright > sright){
48863                 c.scrollLeft = cright-c.clientWidth;
48864             }
48865         }
48866          
48867         return el;
48868     },
48869
48870     updateColumns : function(){
48871         this.grid.stopEditing();
48872         var cm = this.grid.colModel, colIds = this.getColumnIds();
48873         //var totalWidth = cm.getTotalWidth();
48874         var pos = 0;
48875         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48876             //if(cm.isHidden(i)) continue;
48877             var w = cm.getColumnWidth(i);
48878             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48879             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48880         }
48881         this.updateSplitters();
48882     },
48883
48884     generateRules : function(cm){
48885         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48886         Roo.util.CSS.removeStyleSheet(rulesId);
48887         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48888             var cid = cm.getColumnId(i);
48889             var align = '';
48890             if(cm.config[i].align){
48891                 align = 'text-align:'+cm.config[i].align+';';
48892             }
48893             var hidden = '';
48894             if(cm.isHidden(i)){
48895                 hidden = 'display:none;';
48896             }
48897             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48898             ruleBuf.push(
48899                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48900                     this.hdSelector, cid, " {\n", align, width, "}\n",
48901                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48902                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48903         }
48904         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48905     },
48906
48907     updateSplitters : function(){
48908         var cm = this.cm, s = this.getSplitters();
48909         if(s){ // splitters not created yet
48910             var pos = 0, locked = true;
48911             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48912                 if(cm.isHidden(i)) continue;
48913                 var w = cm.getColumnWidth(i); // make sure it's a number
48914                 if(!cm.isLocked(i) && locked){
48915                     pos = 0;
48916                     locked = false;
48917                 }
48918                 pos += w;
48919                 s[i].style.left = (pos-this.splitOffset) + "px";
48920             }
48921         }
48922     },
48923
48924     handleHiddenChange : function(colModel, colIndex, hidden){
48925         if(hidden){
48926             this.hideColumn(colIndex);
48927         }else{
48928             this.unhideColumn(colIndex);
48929         }
48930     },
48931
48932     hideColumn : function(colIndex){
48933         var cid = this.getColumnId(colIndex);
48934         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48935         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48936         if(Roo.isSafari){
48937             this.updateHeaders();
48938         }
48939         this.updateSplitters();
48940         this.layout();
48941     },
48942
48943     unhideColumn : function(colIndex){
48944         var cid = this.getColumnId(colIndex);
48945         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48946         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48947
48948         if(Roo.isSafari){
48949             this.updateHeaders();
48950         }
48951         this.updateSplitters();
48952         this.layout();
48953     },
48954
48955     insertRows : function(dm, firstRow, lastRow, isUpdate){
48956         if(firstRow == 0 && lastRow == dm.getCount()-1){
48957             this.refresh();
48958         }else{
48959             if(!isUpdate){
48960                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48961             }
48962             var s = this.getScrollState();
48963             var markup = this.renderRows(firstRow, lastRow);
48964             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48965             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48966             this.restoreScroll(s);
48967             if(!isUpdate){
48968                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48969                 this.syncRowHeights(firstRow, lastRow);
48970                 this.stripeRows(firstRow);
48971                 this.layout();
48972             }
48973         }
48974     },
48975
48976     bufferRows : function(markup, target, index){
48977         var before = null, trows = target.rows, tbody = target.tBodies[0];
48978         if(index < trows.length){
48979             before = trows[index];
48980         }
48981         var b = document.createElement("div");
48982         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48983         var rows = b.firstChild.rows;
48984         for(var i = 0, len = rows.length; i < len; i++){
48985             if(before){
48986                 tbody.insertBefore(rows[0], before);
48987             }else{
48988                 tbody.appendChild(rows[0]);
48989             }
48990         }
48991         b.innerHTML = "";
48992         b = null;
48993     },
48994
48995     deleteRows : function(dm, firstRow, lastRow){
48996         if(dm.getRowCount()<1){
48997             this.fireEvent("beforerefresh", this);
48998             this.mainBody.update("");
48999             this.lockedBody.update("");
49000             this.fireEvent("refresh", this);
49001         }else{
49002             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49003             var bt = this.getBodyTable();
49004             var tbody = bt.firstChild;
49005             var rows = bt.rows;
49006             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49007                 tbody.removeChild(rows[firstRow]);
49008             }
49009             this.stripeRows(firstRow);
49010             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49011         }
49012     },
49013
49014     updateRows : function(dataSource, firstRow, lastRow){
49015         var s = this.getScrollState();
49016         this.refresh();
49017         this.restoreScroll(s);
49018     },
49019
49020     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49021         if(!noRefresh){
49022            this.refresh();
49023         }
49024         this.updateHeaderSortState();
49025     },
49026
49027     getScrollState : function(){
49028         
49029         var sb = this.scroller.dom;
49030         return {left: sb.scrollLeft, top: sb.scrollTop};
49031     },
49032
49033     stripeRows : function(startRow){
49034         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49035             return;
49036         }
49037         startRow = startRow || 0;
49038         var rows = this.getBodyTable().rows;
49039         var lrows = this.getLockedTable().rows;
49040         var cls = ' x-grid-row-alt ';
49041         for(var i = startRow, len = rows.length; i < len; i++){
49042             var row = rows[i], lrow = lrows[i];
49043             var isAlt = ((i+1) % 2 == 0);
49044             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49045             if(isAlt == hasAlt){
49046                 continue;
49047             }
49048             if(isAlt){
49049                 row.className += " x-grid-row-alt";
49050             }else{
49051                 row.className = row.className.replace("x-grid-row-alt", "");
49052             }
49053             if(lrow){
49054                 lrow.className = row.className;
49055             }
49056         }
49057     },
49058
49059     restoreScroll : function(state){
49060         //Roo.log('GridView.restoreScroll');
49061         var sb = this.scroller.dom;
49062         sb.scrollLeft = state.left;
49063         sb.scrollTop = state.top;
49064         this.syncScroll();
49065     },
49066
49067     syncScroll : function(){
49068         //Roo.log('GridView.syncScroll');
49069         var sb = this.scroller.dom;
49070         var sh = this.mainHd.dom;
49071         var bs = this.mainBody.dom;
49072         var lv = this.lockedBody.dom;
49073         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49074         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49075     },
49076
49077     handleScroll : function(e){
49078         this.syncScroll();
49079         var sb = this.scroller.dom;
49080         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49081         e.stopEvent();
49082     },
49083
49084     handleWheel : function(e){
49085         var d = e.getWheelDelta();
49086         this.scroller.dom.scrollTop -= d*22;
49087         // set this here to prevent jumpy scrolling on large tables
49088         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49089         e.stopEvent();
49090     },
49091
49092     renderRows : function(startRow, endRow){
49093         // pull in all the crap needed to render rows
49094         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49095         var colCount = cm.getColumnCount();
49096
49097         if(ds.getCount() < 1){
49098             return ["", ""];
49099         }
49100
49101         // build a map for all the columns
49102         var cs = [];
49103         for(var i = 0; i < colCount; i++){
49104             var name = cm.getDataIndex(i);
49105             cs[i] = {
49106                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49107                 renderer : cm.getRenderer(i),
49108                 id : cm.getColumnId(i),
49109                 locked : cm.isLocked(i)
49110             };
49111         }
49112
49113         startRow = startRow || 0;
49114         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49115
49116         // records to render
49117         var rs = ds.getRange(startRow, endRow);
49118
49119         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49120     },
49121
49122     // As much as I hate to duplicate code, this was branched because FireFox really hates
49123     // [].join("") on strings. The performance difference was substantial enough to
49124     // branch this function
49125     doRender : Roo.isGecko ?
49126             function(cs, rs, ds, startRow, colCount, stripe){
49127                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49128                 // buffers
49129                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49130                 
49131                 var hasListener = this.grid.hasListener('rowclass');
49132                 var rowcfg = {};
49133                 for(var j = 0, len = rs.length; j < len; j++){
49134                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49135                     for(var i = 0; i < colCount; i++){
49136                         c = cs[i];
49137                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49138                         p.id = c.id;
49139                         p.css = p.attr = "";
49140                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49141                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49142                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49143                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49144                         }
49145                         var markup = ct.apply(p);
49146                         if(!c.locked){
49147                             cb+= markup;
49148                         }else{
49149                             lcb+= markup;
49150                         }
49151                     }
49152                     var alt = [];
49153                     if(stripe && ((rowIndex+1) % 2 == 0)){
49154                         alt.push("x-grid-row-alt")
49155                     }
49156                     if(r.dirty){
49157                         alt.push(  " x-grid-dirty-row");
49158                     }
49159                     rp.cells = lcb;
49160                     if(this.getRowClass){
49161                         alt.push(this.getRowClass(r, rowIndex));
49162                     }
49163                     if (hasListener) {
49164                         rowcfg = {
49165                              
49166                             record: r,
49167                             rowIndex : rowIndex,
49168                             rowClass : ''
49169                         }
49170                         this.grid.fireEvent('rowclass', this, rowcfg);
49171                         alt.push(rowcfg.rowClass);
49172                     }
49173                     rp.alt = alt.join(" ");
49174                     lbuf+= rt.apply(rp);
49175                     rp.cells = cb;
49176                     buf+=  rt.apply(rp);
49177                 }
49178                 return [lbuf, buf];
49179             } :
49180             function(cs, rs, ds, startRow, colCount, stripe){
49181                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49182                 // buffers
49183                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49184                 var hasListener = this.grid.hasListener('rowclass');
49185                 var rowcfg = {};
49186                 for(var j = 0, len = rs.length; j < len; j++){
49187                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49188                     for(var i = 0; i < colCount; i++){
49189                         c = cs[i];
49190                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49191                         p.id = c.id;
49192                         p.css = p.attr = "";
49193                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49194                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49195                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49196                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49197                         }
49198                         var markup = ct.apply(p);
49199                         if(!c.locked){
49200                             cb[cb.length] = markup;
49201                         }else{
49202                             lcb[lcb.length] = markup;
49203                         }
49204                     }
49205                     var alt = [];
49206                     if(stripe && ((rowIndex+1) % 2 == 0)){
49207                         alt.push( "x-grid-row-alt");
49208                     }
49209                     if(r.dirty){
49210                         alt.push(" x-grid-dirty-row");
49211                     }
49212                     rp.cells = lcb;
49213                     if(this.getRowClass){
49214                         alt.push( this.getRowClass(r, rowIndex));
49215                     }
49216                     if (hasListener) {
49217                         rowcfg = {
49218                              
49219                             record: r,
49220                             rowIndex : rowIndex,
49221                             rowClass : ''
49222                         }
49223                         this.grid.fireEvent('rowclass', this, rowcfg);
49224                         alt.push(rowcfg.rowClass);
49225                     }
49226                     rp.alt = alt.join(" ");
49227                     rp.cells = lcb.join("");
49228                     lbuf[lbuf.length] = rt.apply(rp);
49229                     rp.cells = cb.join("");
49230                     buf[buf.length] =  rt.apply(rp);
49231                 }
49232                 return [lbuf.join(""), buf.join("")];
49233             },
49234
49235     renderBody : function(){
49236         var markup = this.renderRows();
49237         var bt = this.templates.body;
49238         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49239     },
49240
49241     /**
49242      * Refreshes the grid
49243      * @param {Boolean} headersToo
49244      */
49245     refresh : function(headersToo){
49246         this.fireEvent("beforerefresh", this);
49247         this.grid.stopEditing();
49248         var result = this.renderBody();
49249         this.lockedBody.update(result[0]);
49250         this.mainBody.update(result[1]);
49251         if(headersToo === true){
49252             this.updateHeaders();
49253             this.updateColumns();
49254             this.updateSplitters();
49255             this.updateHeaderSortState();
49256         }
49257         this.syncRowHeights();
49258         this.layout();
49259         this.fireEvent("refresh", this);
49260     },
49261
49262     handleColumnMove : function(cm, oldIndex, newIndex){
49263         this.indexMap = null;
49264         var s = this.getScrollState();
49265         this.refresh(true);
49266         this.restoreScroll(s);
49267         this.afterMove(newIndex);
49268     },
49269
49270     afterMove : function(colIndex){
49271         if(this.enableMoveAnim && Roo.enableFx){
49272             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49273         }
49274         // if multisort - fix sortOrder, and reload..
49275         if (this.grid.dataSource.multiSort) {
49276             // the we can call sort again..
49277             var dm = this.grid.dataSource;
49278             var cm = this.grid.colModel;
49279             var so = [];
49280             for(var i = 0; i < cm.config.length; i++ ) {
49281                 
49282                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49283                     continue; // dont' bother, it's not in sort list or being set.
49284                 }
49285                 
49286                 so.push(cm.config[i].dataIndex);
49287             };
49288             dm.sortOrder = so;
49289             dm.load(dm.lastOptions);
49290             
49291             
49292         }
49293         
49294     },
49295
49296     updateCell : function(dm, rowIndex, dataIndex){
49297         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49298         if(typeof colIndex == "undefined"){ // not present in grid
49299             return;
49300         }
49301         var cm = this.grid.colModel;
49302         var cell = this.getCell(rowIndex, colIndex);
49303         var cellText = this.getCellText(rowIndex, colIndex);
49304
49305         var p = {
49306             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49307             id : cm.getColumnId(colIndex),
49308             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49309         };
49310         var renderer = cm.getRenderer(colIndex);
49311         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49312         if(typeof val == "undefined" || val === "") val = "&#160;";
49313         cellText.innerHTML = val;
49314         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49315         this.syncRowHeights(rowIndex, rowIndex);
49316     },
49317
49318     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49319         var maxWidth = 0;
49320         if(this.grid.autoSizeHeaders){
49321             var h = this.getHeaderCellMeasure(colIndex);
49322             maxWidth = Math.max(maxWidth, h.scrollWidth);
49323         }
49324         var tb, index;
49325         if(this.cm.isLocked(colIndex)){
49326             tb = this.getLockedTable();
49327             index = colIndex;
49328         }else{
49329             tb = this.getBodyTable();
49330             index = colIndex - this.cm.getLockedCount();
49331         }
49332         if(tb && tb.rows){
49333             var rows = tb.rows;
49334             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49335             for(var i = 0; i < stopIndex; i++){
49336                 var cell = rows[i].childNodes[index].firstChild;
49337                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49338             }
49339         }
49340         return maxWidth + /*margin for error in IE*/ 5;
49341     },
49342     /**
49343      * Autofit a column to its content.
49344      * @param {Number} colIndex
49345      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49346      */
49347      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49348          if(this.cm.isHidden(colIndex)){
49349              return; // can't calc a hidden column
49350          }
49351         if(forceMinSize){
49352             var cid = this.cm.getColumnId(colIndex);
49353             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49354            if(this.grid.autoSizeHeaders){
49355                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49356            }
49357         }
49358         var newWidth = this.calcColumnWidth(colIndex);
49359         this.cm.setColumnWidth(colIndex,
49360             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49361         if(!suppressEvent){
49362             this.grid.fireEvent("columnresize", colIndex, newWidth);
49363         }
49364     },
49365
49366     /**
49367      * Autofits all columns to their content and then expands to fit any extra space in the grid
49368      */
49369      autoSizeColumns : function(){
49370         var cm = this.grid.colModel;
49371         var colCount = cm.getColumnCount();
49372         for(var i = 0; i < colCount; i++){
49373             this.autoSizeColumn(i, true, true);
49374         }
49375         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49376             this.fitColumns();
49377         }else{
49378             this.updateColumns();
49379             this.layout();
49380         }
49381     },
49382
49383     /**
49384      * Autofits all columns to the grid's width proportionate with their current size
49385      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49386      */
49387     fitColumns : function(reserveScrollSpace){
49388         var cm = this.grid.colModel;
49389         var colCount = cm.getColumnCount();
49390         var cols = [];
49391         var width = 0;
49392         var i, w;
49393         for (i = 0; i < colCount; i++){
49394             if(!cm.isHidden(i) && !cm.isFixed(i)){
49395                 w = cm.getColumnWidth(i);
49396                 cols.push(i);
49397                 cols.push(w);
49398                 width += w;
49399             }
49400         }
49401         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49402         if(reserveScrollSpace){
49403             avail -= 17;
49404         }
49405         var frac = (avail - cm.getTotalWidth())/width;
49406         while (cols.length){
49407             w = cols.pop();
49408             i = cols.pop();
49409             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49410         }
49411         this.updateColumns();
49412         this.layout();
49413     },
49414
49415     onRowSelect : function(rowIndex){
49416         var row = this.getRowComposite(rowIndex);
49417         row.addClass("x-grid-row-selected");
49418     },
49419
49420     onRowDeselect : function(rowIndex){
49421         var row = this.getRowComposite(rowIndex);
49422         row.removeClass("x-grid-row-selected");
49423     },
49424
49425     onCellSelect : function(row, col){
49426         var cell = this.getCell(row, col);
49427         if(cell){
49428             Roo.fly(cell).addClass("x-grid-cell-selected");
49429         }
49430     },
49431
49432     onCellDeselect : function(row, col){
49433         var cell = this.getCell(row, col);
49434         if(cell){
49435             Roo.fly(cell).removeClass("x-grid-cell-selected");
49436         }
49437     },
49438
49439     updateHeaderSortState : function(){
49440         
49441         // sort state can be single { field: xxx, direction : yyy}
49442         // or   { xxx=>ASC , yyy : DESC ..... }
49443         
49444         var mstate = {};
49445         if (!this.ds.multiSort) { 
49446             var state = this.ds.getSortState();
49447             if(!state){
49448                 return;
49449             }
49450             mstate[state.field] = state.direction;
49451             // FIXME... - this is not used here.. but might be elsewhere..
49452             this.sortState = state;
49453             
49454         } else {
49455             mstate = this.ds.sortToggle;
49456         }
49457         //remove existing sort classes..
49458         
49459         var sc = this.sortClasses;
49460         var hds = this.el.select(this.headerSelector).removeClass(sc);
49461         
49462         for(var f in mstate) {
49463         
49464             var sortColumn = this.cm.findColumnIndex(f);
49465             
49466             if(sortColumn != -1){
49467                 var sortDir = mstate[f];        
49468                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49469             }
49470         }
49471         
49472          
49473         
49474     },
49475
49476
49477     handleHeaderClick : function(g, index){
49478         if(this.headersDisabled){
49479             return;
49480         }
49481         var dm = g.dataSource, cm = g.colModel;
49482         if(!cm.isSortable(index)){
49483             return;
49484         }
49485         g.stopEditing();
49486         
49487         if (dm.multiSort) {
49488             // update the sortOrder
49489             var so = [];
49490             for(var i = 0; i < cm.config.length; i++ ) {
49491                 
49492                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49493                     continue; // dont' bother, it's not in sort list or being set.
49494                 }
49495                 
49496                 so.push(cm.config[i].dataIndex);
49497             };
49498             dm.sortOrder = so;
49499         }
49500         
49501         
49502         dm.sort(cm.getDataIndex(index));
49503     },
49504
49505
49506     destroy : function(){
49507         if(this.colMenu){
49508             this.colMenu.removeAll();
49509             Roo.menu.MenuMgr.unregister(this.colMenu);
49510             this.colMenu.getEl().remove();
49511             delete this.colMenu;
49512         }
49513         if(this.hmenu){
49514             this.hmenu.removeAll();
49515             Roo.menu.MenuMgr.unregister(this.hmenu);
49516             this.hmenu.getEl().remove();
49517             delete this.hmenu;
49518         }
49519         if(this.grid.enableColumnMove){
49520             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49521             if(dds){
49522                 for(var dd in dds){
49523                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49524                         var elid = dds[dd].dragElId;
49525                         dds[dd].unreg();
49526                         Roo.get(elid).remove();
49527                     } else if(dds[dd].config.isTarget){
49528                         dds[dd].proxyTop.remove();
49529                         dds[dd].proxyBottom.remove();
49530                         dds[dd].unreg();
49531                     }
49532                     if(Roo.dd.DDM.locationCache[dd]){
49533                         delete Roo.dd.DDM.locationCache[dd];
49534                     }
49535                 }
49536                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49537             }
49538         }
49539         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49540         this.bind(null, null);
49541         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49542     },
49543
49544     handleLockChange : function(){
49545         this.refresh(true);
49546     },
49547
49548     onDenyColumnLock : function(){
49549
49550     },
49551
49552     onDenyColumnHide : function(){
49553
49554     },
49555
49556     handleHdMenuClick : function(item){
49557         var index = this.hdCtxIndex;
49558         var cm = this.cm, ds = this.ds;
49559         switch(item.id){
49560             case "asc":
49561                 ds.sort(cm.getDataIndex(index), "ASC");
49562                 break;
49563             case "desc":
49564                 ds.sort(cm.getDataIndex(index), "DESC");
49565                 break;
49566             case "lock":
49567                 var lc = cm.getLockedCount();
49568                 if(cm.getColumnCount(true) <= lc+1){
49569                     this.onDenyColumnLock();
49570                     return;
49571                 }
49572                 if(lc != index){
49573                     cm.setLocked(index, true, true);
49574                     cm.moveColumn(index, lc);
49575                     this.grid.fireEvent("columnmove", index, lc);
49576                 }else{
49577                     cm.setLocked(index, true);
49578                 }
49579             break;
49580             case "unlock":
49581                 var lc = cm.getLockedCount();
49582                 if((lc-1) != index){
49583                     cm.setLocked(index, false, true);
49584                     cm.moveColumn(index, lc-1);
49585                     this.grid.fireEvent("columnmove", index, lc-1);
49586                 }else{
49587                     cm.setLocked(index, false);
49588                 }
49589             break;
49590             default:
49591                 index = cm.getIndexById(item.id.substr(4));
49592                 if(index != -1){
49593                     if(item.checked && cm.getColumnCount(true) <= 1){
49594                         this.onDenyColumnHide();
49595                         return false;
49596                     }
49597                     cm.setHidden(index, item.checked);
49598                 }
49599         }
49600         return true;
49601     },
49602
49603     beforeColMenuShow : function(){
49604         var cm = this.cm,  colCount = cm.getColumnCount();
49605         this.colMenu.removeAll();
49606         for(var i = 0; i < colCount; i++){
49607             this.colMenu.add(new Roo.menu.CheckItem({
49608                 id: "col-"+cm.getColumnId(i),
49609                 text: cm.getColumnHeader(i),
49610                 checked: !cm.isHidden(i),
49611                 hideOnClick:false
49612             }));
49613         }
49614     },
49615
49616     handleHdCtx : function(g, index, e){
49617         e.stopEvent();
49618         var hd = this.getHeaderCell(index);
49619         this.hdCtxIndex = index;
49620         var ms = this.hmenu.items, cm = this.cm;
49621         ms.get("asc").setDisabled(!cm.isSortable(index));
49622         ms.get("desc").setDisabled(!cm.isSortable(index));
49623         if(this.grid.enableColLock !== false){
49624             ms.get("lock").setDisabled(cm.isLocked(index));
49625             ms.get("unlock").setDisabled(!cm.isLocked(index));
49626         }
49627         this.hmenu.show(hd, "tl-bl");
49628     },
49629
49630     handleHdOver : function(e){
49631         var hd = this.findHeaderCell(e.getTarget());
49632         if(hd && !this.headersDisabled){
49633             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49634                this.fly(hd).addClass("x-grid-hd-over");
49635             }
49636         }
49637     },
49638
49639     handleHdOut : function(e){
49640         var hd = this.findHeaderCell(e.getTarget());
49641         if(hd){
49642             this.fly(hd).removeClass("x-grid-hd-over");
49643         }
49644     },
49645
49646     handleSplitDblClick : function(e, t){
49647         var i = this.getCellIndex(t);
49648         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49649             this.autoSizeColumn(i, true);
49650             this.layout();
49651         }
49652     },
49653
49654     render : function(){
49655
49656         var cm = this.cm;
49657         var colCount = cm.getColumnCount();
49658
49659         if(this.grid.monitorWindowResize === true){
49660             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49661         }
49662         var header = this.renderHeaders();
49663         var body = this.templates.body.apply({rows:""});
49664         var html = this.templates.master.apply({
49665             lockedBody: body,
49666             body: body,
49667             lockedHeader: header[0],
49668             header: header[1]
49669         });
49670
49671         //this.updateColumns();
49672
49673         this.grid.getGridEl().dom.innerHTML = html;
49674
49675         this.initElements();
49676         
49677         // a kludge to fix the random scolling effect in webkit
49678         this.el.on("scroll", function() {
49679             this.el.dom.scrollTop=0; // hopefully not recursive..
49680         },this);
49681
49682         this.scroller.on("scroll", this.handleScroll, this);
49683         this.lockedBody.on("mousewheel", this.handleWheel, this);
49684         this.mainBody.on("mousewheel", this.handleWheel, this);
49685
49686         this.mainHd.on("mouseover", this.handleHdOver, this);
49687         this.mainHd.on("mouseout", this.handleHdOut, this);
49688         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49689                 {delegate: "."+this.splitClass});
49690
49691         this.lockedHd.on("mouseover", this.handleHdOver, this);
49692         this.lockedHd.on("mouseout", this.handleHdOut, this);
49693         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49694                 {delegate: "."+this.splitClass});
49695
49696         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49697             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49698         }
49699
49700         this.updateSplitters();
49701
49702         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49703             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49704             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49705         }
49706
49707         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49708             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49709             this.hmenu.add(
49710                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49711                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49712             );
49713             if(this.grid.enableColLock !== false){
49714                 this.hmenu.add('-',
49715                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49716                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49717                 );
49718             }
49719             if(this.grid.enableColumnHide !== false){
49720
49721                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49722                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49723                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49724
49725                 this.hmenu.add('-',
49726                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49727                 );
49728             }
49729             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49730
49731             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49732         }
49733
49734         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49735             this.dd = new Roo.grid.GridDragZone(this.grid, {
49736                 ddGroup : this.grid.ddGroup || 'GridDD'
49737             });
49738         }
49739
49740         /*
49741         for(var i = 0; i < colCount; i++){
49742             if(cm.isHidden(i)){
49743                 this.hideColumn(i);
49744             }
49745             if(cm.config[i].align){
49746                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49747                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49748             }
49749         }*/
49750         
49751         this.updateHeaderSortState();
49752
49753         this.beforeInitialResize();
49754         this.layout(true);
49755
49756         // two part rendering gives faster view to the user
49757         this.renderPhase2.defer(1, this);
49758     },
49759
49760     renderPhase2 : function(){
49761         // render the rows now
49762         this.refresh();
49763         if(this.grid.autoSizeColumns){
49764             this.autoSizeColumns();
49765         }
49766     },
49767
49768     beforeInitialResize : function(){
49769
49770     },
49771
49772     onColumnSplitterMoved : function(i, w){
49773         this.userResized = true;
49774         var cm = this.grid.colModel;
49775         cm.setColumnWidth(i, w, true);
49776         var cid = cm.getColumnId(i);
49777         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49778         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49779         this.updateSplitters();
49780         this.layout();
49781         this.grid.fireEvent("columnresize", i, w);
49782     },
49783
49784     syncRowHeights : function(startIndex, endIndex){
49785         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49786             startIndex = startIndex || 0;
49787             var mrows = this.getBodyTable().rows;
49788             var lrows = this.getLockedTable().rows;
49789             var len = mrows.length-1;
49790             endIndex = Math.min(endIndex || len, len);
49791             for(var i = startIndex; i <= endIndex; i++){
49792                 var m = mrows[i], l = lrows[i];
49793                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49794                 m.style.height = l.style.height = h + "px";
49795             }
49796         }
49797     },
49798
49799     layout : function(initialRender, is2ndPass){
49800         var g = this.grid;
49801         var auto = g.autoHeight;
49802         var scrollOffset = 16;
49803         var c = g.getGridEl(), cm = this.cm,
49804                 expandCol = g.autoExpandColumn,
49805                 gv = this;
49806         //c.beginMeasure();
49807
49808         if(!c.dom.offsetWidth){ // display:none?
49809             if(initialRender){
49810                 this.lockedWrap.show();
49811                 this.mainWrap.show();
49812             }
49813             return;
49814         }
49815
49816         var hasLock = this.cm.isLocked(0);
49817
49818         var tbh = this.headerPanel.getHeight();
49819         var bbh = this.footerPanel.getHeight();
49820
49821         if(auto){
49822             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49823             var newHeight = ch + c.getBorderWidth("tb");
49824             if(g.maxHeight){
49825                 newHeight = Math.min(g.maxHeight, newHeight);
49826             }
49827             c.setHeight(newHeight);
49828         }
49829
49830         if(g.autoWidth){
49831             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49832         }
49833
49834         var s = this.scroller;
49835
49836         var csize = c.getSize(true);
49837
49838         this.el.setSize(csize.width, csize.height);
49839
49840         this.headerPanel.setWidth(csize.width);
49841         this.footerPanel.setWidth(csize.width);
49842
49843         var hdHeight = this.mainHd.getHeight();
49844         var vw = csize.width;
49845         var vh = csize.height - (tbh + bbh);
49846
49847         s.setSize(vw, vh);
49848
49849         var bt = this.getBodyTable();
49850         var ltWidth = hasLock ?
49851                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49852
49853         var scrollHeight = bt.offsetHeight;
49854         var scrollWidth = ltWidth + bt.offsetWidth;
49855         var vscroll = false, hscroll = false;
49856
49857         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49858
49859         var lw = this.lockedWrap, mw = this.mainWrap;
49860         var lb = this.lockedBody, mb = this.mainBody;
49861
49862         setTimeout(function(){
49863             var t = s.dom.offsetTop;
49864             var w = s.dom.clientWidth,
49865                 h = s.dom.clientHeight;
49866
49867             lw.setTop(t);
49868             lw.setSize(ltWidth, h);
49869
49870             mw.setLeftTop(ltWidth, t);
49871             mw.setSize(w-ltWidth, h);
49872
49873             lb.setHeight(h-hdHeight);
49874             mb.setHeight(h-hdHeight);
49875
49876             if(is2ndPass !== true && !gv.userResized && expandCol){
49877                 // high speed resize without full column calculation
49878                 
49879                 var ci = cm.getIndexById(expandCol);
49880                 if (ci < 0) {
49881                     ci = cm.findColumnIndex(expandCol);
49882                 }
49883                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49884                 var expandId = cm.getColumnId(ci);
49885                 var  tw = cm.getTotalWidth(false);
49886                 var currentWidth = cm.getColumnWidth(ci);
49887                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49888                 if(currentWidth != cw){
49889                     cm.setColumnWidth(ci, cw, true);
49890                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49891                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49892                     gv.updateSplitters();
49893                     gv.layout(false, true);
49894                 }
49895             }
49896
49897             if(initialRender){
49898                 lw.show();
49899                 mw.show();
49900             }
49901             //c.endMeasure();
49902         }, 10);
49903     },
49904
49905     onWindowResize : function(){
49906         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49907             return;
49908         }
49909         this.layout();
49910     },
49911
49912     appendFooter : function(parentEl){
49913         return null;
49914     },
49915
49916     sortAscText : "Sort Ascending",
49917     sortDescText : "Sort Descending",
49918     lockText : "Lock Column",
49919     unlockText : "Unlock Column",
49920     columnsText : "Columns"
49921 });
49922
49923
49924 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49925     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49926     this.proxy.el.addClass('x-grid3-col-dd');
49927 };
49928
49929 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49930     handleMouseDown : function(e){
49931
49932     },
49933
49934     callHandleMouseDown : function(e){
49935         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49936     }
49937 });
49938 /*
49939  * Based on:
49940  * Ext JS Library 1.1.1
49941  * Copyright(c) 2006-2007, Ext JS, LLC.
49942  *
49943  * Originally Released Under LGPL - original licence link has changed is not relivant.
49944  *
49945  * Fork - LGPL
49946  * <script type="text/javascript">
49947  */
49948  
49949 // private
49950 // This is a support class used internally by the Grid components
49951 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49952     this.grid = grid;
49953     this.view = grid.getView();
49954     this.proxy = this.view.resizeProxy;
49955     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49956         "gridSplitters" + this.grid.getGridEl().id, {
49957         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49958     });
49959     this.setHandleElId(Roo.id(hd));
49960     this.setOuterHandleElId(Roo.id(hd2));
49961     this.scroll = false;
49962 };
49963 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49964     fly: Roo.Element.fly,
49965
49966     b4StartDrag : function(x, y){
49967         this.view.headersDisabled = true;
49968         this.proxy.setHeight(this.view.mainWrap.getHeight());
49969         var w = this.cm.getColumnWidth(this.cellIndex);
49970         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49971         this.resetConstraints();
49972         this.setXConstraint(minw, 1000);
49973         this.setYConstraint(0, 0);
49974         this.minX = x - minw;
49975         this.maxX = x + 1000;
49976         this.startPos = x;
49977         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49978     },
49979
49980
49981     handleMouseDown : function(e){
49982         ev = Roo.EventObject.setEvent(e);
49983         var t = this.fly(ev.getTarget());
49984         if(t.hasClass("x-grid-split")){
49985             this.cellIndex = this.view.getCellIndex(t.dom);
49986             this.split = t.dom;
49987             this.cm = this.grid.colModel;
49988             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49989                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49990             }
49991         }
49992     },
49993
49994     endDrag : function(e){
49995         this.view.headersDisabled = false;
49996         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49997         var diff = endX - this.startPos;
49998         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49999     },
50000
50001     autoOffset : function(){
50002         this.setDelta(0,0);
50003     }
50004 });/*
50005  * Based on:
50006  * Ext JS Library 1.1.1
50007  * Copyright(c) 2006-2007, Ext JS, LLC.
50008  *
50009  * Originally Released Under LGPL - original licence link has changed is not relivant.
50010  *
50011  * Fork - LGPL
50012  * <script type="text/javascript">
50013  */
50014  
50015 // private
50016 // This is a support class used internally by the Grid components
50017 Roo.grid.GridDragZone = function(grid, config){
50018     this.view = grid.getView();
50019     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50020     if(this.view.lockedBody){
50021         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50022         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50023     }
50024     this.scroll = false;
50025     this.grid = grid;
50026     this.ddel = document.createElement('div');
50027     this.ddel.className = 'x-grid-dd-wrap';
50028 };
50029
50030 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50031     ddGroup : "GridDD",
50032
50033     getDragData : function(e){
50034         var t = Roo.lib.Event.getTarget(e);
50035         var rowIndex = this.view.findRowIndex(t);
50036         if(rowIndex !== false){
50037             var sm = this.grid.selModel;
50038             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50039               //  sm.mouseDown(e, t);
50040             //}
50041             if (e.hasModifier()){
50042                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50043             }
50044             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50045         }
50046         return false;
50047     },
50048
50049     onInitDrag : function(e){
50050         var data = this.dragData;
50051         this.ddel.innerHTML = this.grid.getDragDropText();
50052         this.proxy.update(this.ddel);
50053         // fire start drag?
50054     },
50055
50056     afterRepair : function(){
50057         this.dragging = false;
50058     },
50059
50060     getRepairXY : function(e, data){
50061         return false;
50062     },
50063
50064     onEndDrag : function(data, e){
50065         // fire end drag?
50066     },
50067
50068     onValidDrop : function(dd, e, id){
50069         // fire drag drop?
50070         this.hideProxy();
50071     },
50072
50073     beforeInvalidDrop : function(e, id){
50074
50075     }
50076 });/*
50077  * Based on:
50078  * Ext JS Library 1.1.1
50079  * Copyright(c) 2006-2007, Ext JS, LLC.
50080  *
50081  * Originally Released Under LGPL - original licence link has changed is not relivant.
50082  *
50083  * Fork - LGPL
50084  * <script type="text/javascript">
50085  */
50086  
50087
50088 /**
50089  * @class Roo.grid.ColumnModel
50090  * @extends Roo.util.Observable
50091  * This is the default implementation of a ColumnModel used by the Grid. It defines
50092  * the columns in the grid.
50093  * <br>Usage:<br>
50094  <pre><code>
50095  var colModel = new Roo.grid.ColumnModel([
50096         {header: "Ticker", width: 60, sortable: true, locked: true},
50097         {header: "Company Name", width: 150, sortable: true},
50098         {header: "Market Cap.", width: 100, sortable: true},
50099         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50100         {header: "Employees", width: 100, sortable: true, resizable: false}
50101  ]);
50102  </code></pre>
50103  * <p>
50104  
50105  * The config options listed for this class are options which may appear in each
50106  * individual column definition.
50107  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50108  * @constructor
50109  * @param {Object} config An Array of column config objects. See this class's
50110  * config objects for details.
50111 */
50112 Roo.grid.ColumnModel = function(config){
50113         /**
50114      * The config passed into the constructor
50115      */
50116     this.config = config;
50117     this.lookup = {};
50118
50119     // if no id, create one
50120     // if the column does not have a dataIndex mapping,
50121     // map it to the order it is in the config
50122     for(var i = 0, len = config.length; i < len; i++){
50123         var c = config[i];
50124         if(typeof c.dataIndex == "undefined"){
50125             c.dataIndex = i;
50126         }
50127         if(typeof c.renderer == "string"){
50128             c.renderer = Roo.util.Format[c.renderer];
50129         }
50130         if(typeof c.id == "undefined"){
50131             c.id = Roo.id();
50132         }
50133         if(c.editor && c.editor.xtype){
50134             c.editor  = Roo.factory(c.editor, Roo.grid);
50135         }
50136         if(c.editor && c.editor.isFormField){
50137             c.editor = new Roo.grid.GridEditor(c.editor);
50138         }
50139         this.lookup[c.id] = c;
50140     }
50141
50142     /**
50143      * The width of columns which have no width specified (defaults to 100)
50144      * @type Number
50145      */
50146     this.defaultWidth = 100;
50147
50148     /**
50149      * Default sortable of columns which have no sortable specified (defaults to false)
50150      * @type Boolean
50151      */
50152     this.defaultSortable = false;
50153
50154     this.addEvents({
50155         /**
50156              * @event widthchange
50157              * Fires when the width of a column changes.
50158              * @param {ColumnModel} this
50159              * @param {Number} columnIndex The column index
50160              * @param {Number} newWidth The new width
50161              */
50162             "widthchange": true,
50163         /**
50164              * @event headerchange
50165              * Fires when the text of a header changes.
50166              * @param {ColumnModel} this
50167              * @param {Number} columnIndex The column index
50168              * @param {Number} newText The new header text
50169              */
50170             "headerchange": true,
50171         /**
50172              * @event hiddenchange
50173              * Fires when a column is hidden or "unhidden".
50174              * @param {ColumnModel} this
50175              * @param {Number} columnIndex The column index
50176              * @param {Boolean} hidden true if hidden, false otherwise
50177              */
50178             "hiddenchange": true,
50179             /**
50180          * @event columnmoved
50181          * Fires when a column is moved.
50182          * @param {ColumnModel} this
50183          * @param {Number} oldIndex
50184          * @param {Number} newIndex
50185          */
50186         "columnmoved" : true,
50187         /**
50188          * @event columlockchange
50189          * Fires when a column's locked state is changed
50190          * @param {ColumnModel} this
50191          * @param {Number} colIndex
50192          * @param {Boolean} locked true if locked
50193          */
50194         "columnlockchange" : true
50195     });
50196     Roo.grid.ColumnModel.superclass.constructor.call(this);
50197 };
50198 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50199     /**
50200      * @cfg {String} header The header text to display in the Grid view.
50201      */
50202     /**
50203      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50204      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50205      * specified, the column's index is used as an index into the Record's data Array.
50206      */
50207     /**
50208      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50209      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50210      */
50211     /**
50212      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50213      * Defaults to the value of the {@link #defaultSortable} property.
50214      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50215      */
50216     /**
50217      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50218      */
50219     /**
50220      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50221      */
50222     /**
50223      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50224      */
50225     /**
50226      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50227      */
50228     /**
50229      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50230      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50231      * default renderer uses the raw data value.
50232      */
50233        /**
50234      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50235      */
50236     /**
50237      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50238      */
50239
50240     /**
50241      * Returns the id of the column at the specified index.
50242      * @param {Number} index The column index
50243      * @return {String} the id
50244      */
50245     getColumnId : function(index){
50246         return this.config[index].id;
50247     },
50248
50249     /**
50250      * Returns the column for a specified id.
50251      * @param {String} id The column id
50252      * @return {Object} the column
50253      */
50254     getColumnById : function(id){
50255         return this.lookup[id];
50256     },
50257
50258     
50259     /**
50260      * Returns the column for a specified dataIndex.
50261      * @param {String} dataIndex The column dataIndex
50262      * @return {Object|Boolean} the column or false if not found
50263      */
50264     getColumnByDataIndex: function(dataIndex){
50265         var index = this.findColumnIndex(dataIndex);
50266         return index > -1 ? this.config[index] : false;
50267     },
50268     
50269     /**
50270      * Returns the index for a specified column id.
50271      * @param {String} id The column id
50272      * @return {Number} the index, or -1 if not found
50273      */
50274     getIndexById : function(id){
50275         for(var i = 0, len = this.config.length; i < len; i++){
50276             if(this.config[i].id == id){
50277                 return i;
50278             }
50279         }
50280         return -1;
50281     },
50282     
50283     /**
50284      * Returns the index for a specified column dataIndex.
50285      * @param {String} dataIndex The column dataIndex
50286      * @return {Number} the index, or -1 if not found
50287      */
50288     
50289     findColumnIndex : function(dataIndex){
50290         for(var i = 0, len = this.config.length; i < len; i++){
50291             if(this.config[i].dataIndex == dataIndex){
50292                 return i;
50293             }
50294         }
50295         return -1;
50296     },
50297     
50298     
50299     moveColumn : function(oldIndex, newIndex){
50300         var c = this.config[oldIndex];
50301         this.config.splice(oldIndex, 1);
50302         this.config.splice(newIndex, 0, c);
50303         this.dataMap = null;
50304         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50305     },
50306
50307     isLocked : function(colIndex){
50308         return this.config[colIndex].locked === true;
50309     },
50310
50311     setLocked : function(colIndex, value, suppressEvent){
50312         if(this.isLocked(colIndex) == value){
50313             return;
50314         }
50315         this.config[colIndex].locked = value;
50316         if(!suppressEvent){
50317             this.fireEvent("columnlockchange", this, colIndex, value);
50318         }
50319     },
50320
50321     getTotalLockedWidth : function(){
50322         var totalWidth = 0;
50323         for(var i = 0; i < this.config.length; i++){
50324             if(this.isLocked(i) && !this.isHidden(i)){
50325                 this.totalWidth += this.getColumnWidth(i);
50326             }
50327         }
50328         return totalWidth;
50329     },
50330
50331     getLockedCount : function(){
50332         for(var i = 0, len = this.config.length; i < len; i++){
50333             if(!this.isLocked(i)){
50334                 return i;
50335             }
50336         }
50337     },
50338
50339     /**
50340      * Returns the number of columns.
50341      * @return {Number}
50342      */
50343     getColumnCount : function(visibleOnly){
50344         if(visibleOnly === true){
50345             var c = 0;
50346             for(var i = 0, len = this.config.length; i < len; i++){
50347                 if(!this.isHidden(i)){
50348                     c++;
50349                 }
50350             }
50351             return c;
50352         }
50353         return this.config.length;
50354     },
50355
50356     /**
50357      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50358      * @param {Function} fn
50359      * @param {Object} scope (optional)
50360      * @return {Array} result
50361      */
50362     getColumnsBy : function(fn, scope){
50363         var r = [];
50364         for(var i = 0, len = this.config.length; i < len; i++){
50365             var c = this.config[i];
50366             if(fn.call(scope||this, c, i) === true){
50367                 r[r.length] = c;
50368             }
50369         }
50370         return r;
50371     },
50372
50373     /**
50374      * Returns true if the specified column is sortable.
50375      * @param {Number} col The column index
50376      * @return {Boolean}
50377      */
50378     isSortable : function(col){
50379         if(typeof this.config[col].sortable == "undefined"){
50380             return this.defaultSortable;
50381         }
50382         return this.config[col].sortable;
50383     },
50384
50385     /**
50386      * Returns the rendering (formatting) function defined for the column.
50387      * @param {Number} col The column index.
50388      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50389      */
50390     getRenderer : function(col){
50391         if(!this.config[col].renderer){
50392             return Roo.grid.ColumnModel.defaultRenderer;
50393         }
50394         return this.config[col].renderer;
50395     },
50396
50397     /**
50398      * Sets the rendering (formatting) function for a column.
50399      * @param {Number} col The column index
50400      * @param {Function} fn The function to use to process the cell's raw data
50401      * to return HTML markup for the grid view. The render function is called with
50402      * the following parameters:<ul>
50403      * <li>Data value.</li>
50404      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50405      * <li>css A CSS style string to apply to the table cell.</li>
50406      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50407      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50408      * <li>Row index</li>
50409      * <li>Column index</li>
50410      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50411      */
50412     setRenderer : function(col, fn){
50413         this.config[col].renderer = fn;
50414     },
50415
50416     /**
50417      * Returns the width for the specified column.
50418      * @param {Number} col The column index
50419      * @return {Number}
50420      */
50421     getColumnWidth : function(col){
50422         return this.config[col].width * 1 || this.defaultWidth;
50423     },
50424
50425     /**
50426      * Sets the width for a column.
50427      * @param {Number} col The column index
50428      * @param {Number} width The new width
50429      */
50430     setColumnWidth : function(col, width, suppressEvent){
50431         this.config[col].width = width;
50432         this.totalWidth = null;
50433         if(!suppressEvent){
50434              this.fireEvent("widthchange", this, col, width);
50435         }
50436     },
50437
50438     /**
50439      * Returns the total width of all columns.
50440      * @param {Boolean} includeHidden True to include hidden column widths
50441      * @return {Number}
50442      */
50443     getTotalWidth : function(includeHidden){
50444         if(!this.totalWidth){
50445             this.totalWidth = 0;
50446             for(var i = 0, len = this.config.length; i < len; i++){
50447                 if(includeHidden || !this.isHidden(i)){
50448                     this.totalWidth += this.getColumnWidth(i);
50449                 }
50450             }
50451         }
50452         return this.totalWidth;
50453     },
50454
50455     /**
50456      * Returns the header for the specified column.
50457      * @param {Number} col The column index
50458      * @return {String}
50459      */
50460     getColumnHeader : function(col){
50461         return this.config[col].header;
50462     },
50463
50464     /**
50465      * Sets the header for a column.
50466      * @param {Number} col The column index
50467      * @param {String} header The new header
50468      */
50469     setColumnHeader : function(col, header){
50470         this.config[col].header = header;
50471         this.fireEvent("headerchange", this, col, header);
50472     },
50473
50474     /**
50475      * Returns the tooltip for the specified column.
50476      * @param {Number} col The column index
50477      * @return {String}
50478      */
50479     getColumnTooltip : function(col){
50480             return this.config[col].tooltip;
50481     },
50482     /**
50483      * Sets the tooltip for a column.
50484      * @param {Number} col The column index
50485      * @param {String} tooltip The new tooltip
50486      */
50487     setColumnTooltip : function(col, tooltip){
50488             this.config[col].tooltip = tooltip;
50489     },
50490
50491     /**
50492      * Returns the dataIndex for the specified column.
50493      * @param {Number} col The column index
50494      * @return {Number}
50495      */
50496     getDataIndex : function(col){
50497         return this.config[col].dataIndex;
50498     },
50499
50500     /**
50501      * Sets the dataIndex for a column.
50502      * @param {Number} col The column index
50503      * @param {Number} dataIndex The new dataIndex
50504      */
50505     setDataIndex : function(col, dataIndex){
50506         this.config[col].dataIndex = dataIndex;
50507     },
50508
50509     
50510     
50511     /**
50512      * Returns true if the cell is editable.
50513      * @param {Number} colIndex The column index
50514      * @param {Number} rowIndex The row index
50515      * @return {Boolean}
50516      */
50517     isCellEditable : function(colIndex, rowIndex){
50518         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50519     },
50520
50521     /**
50522      * Returns the editor defined for the cell/column.
50523      * return false or null to disable editing.
50524      * @param {Number} colIndex The column index
50525      * @param {Number} rowIndex The row index
50526      * @return {Object}
50527      */
50528     getCellEditor : function(colIndex, rowIndex){
50529         return this.config[colIndex].editor;
50530     },
50531
50532     /**
50533      * Sets if a column is editable.
50534      * @param {Number} col The column index
50535      * @param {Boolean} editable True if the column is editable
50536      */
50537     setEditable : function(col, editable){
50538         this.config[col].editable = editable;
50539     },
50540
50541
50542     /**
50543      * Returns true if the column is hidden.
50544      * @param {Number} colIndex The column index
50545      * @return {Boolean}
50546      */
50547     isHidden : function(colIndex){
50548         return this.config[colIndex].hidden;
50549     },
50550
50551
50552     /**
50553      * Returns true if the column width cannot be changed
50554      */
50555     isFixed : function(colIndex){
50556         return this.config[colIndex].fixed;
50557     },
50558
50559     /**
50560      * Returns true if the column can be resized
50561      * @return {Boolean}
50562      */
50563     isResizable : function(colIndex){
50564         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50565     },
50566     /**
50567      * Sets if a column is hidden.
50568      * @param {Number} colIndex The column index
50569      * @param {Boolean} hidden True if the column is hidden
50570      */
50571     setHidden : function(colIndex, hidden){
50572         this.config[colIndex].hidden = hidden;
50573         this.totalWidth = null;
50574         this.fireEvent("hiddenchange", this, colIndex, hidden);
50575     },
50576
50577     /**
50578      * Sets the editor for a column.
50579      * @param {Number} col The column index
50580      * @param {Object} editor The editor object
50581      */
50582     setEditor : function(col, editor){
50583         this.config[col].editor = editor;
50584     }
50585 });
50586
50587 Roo.grid.ColumnModel.defaultRenderer = function(value){
50588         if(typeof value == "string" && value.length < 1){
50589             return "&#160;";
50590         }
50591         return value;
50592 };
50593
50594 // Alias for backwards compatibility
50595 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50596 /*
50597  * Based on:
50598  * Ext JS Library 1.1.1
50599  * Copyright(c) 2006-2007, Ext JS, LLC.
50600  *
50601  * Originally Released Under LGPL - original licence link has changed is not relivant.
50602  *
50603  * Fork - LGPL
50604  * <script type="text/javascript">
50605  */
50606
50607 /**
50608  * @class Roo.grid.AbstractSelectionModel
50609  * @extends Roo.util.Observable
50610  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50611  * implemented by descendant classes.  This class should not be directly instantiated.
50612  * @constructor
50613  */
50614 Roo.grid.AbstractSelectionModel = function(){
50615     this.locked = false;
50616     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50617 };
50618
50619 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50620     /** @ignore Called by the grid automatically. Do not call directly. */
50621     init : function(grid){
50622         this.grid = grid;
50623         this.initEvents();
50624     },
50625
50626     /**
50627      * Locks the selections.
50628      */
50629     lock : function(){
50630         this.locked = true;
50631     },
50632
50633     /**
50634      * Unlocks the selections.
50635      */
50636     unlock : function(){
50637         this.locked = false;
50638     },
50639
50640     /**
50641      * Returns true if the selections are locked.
50642      * @return {Boolean}
50643      */
50644     isLocked : function(){
50645         return this.locked;
50646     }
50647 });/*
50648  * Based on:
50649  * Ext JS Library 1.1.1
50650  * Copyright(c) 2006-2007, Ext JS, LLC.
50651  *
50652  * Originally Released Under LGPL - original licence link has changed is not relivant.
50653  *
50654  * Fork - LGPL
50655  * <script type="text/javascript">
50656  */
50657 /**
50658  * @extends Roo.grid.AbstractSelectionModel
50659  * @class Roo.grid.RowSelectionModel
50660  * The default SelectionModel used by {@link Roo.grid.Grid}.
50661  * It supports multiple selections and keyboard selection/navigation. 
50662  * @constructor
50663  * @param {Object} config
50664  */
50665 Roo.grid.RowSelectionModel = function(config){
50666     Roo.apply(this, config);
50667     this.selections = new Roo.util.MixedCollection(false, function(o){
50668         return o.id;
50669     });
50670
50671     this.last = false;
50672     this.lastActive = false;
50673
50674     this.addEvents({
50675         /**
50676              * @event selectionchange
50677              * Fires when the selection changes
50678              * @param {SelectionModel} this
50679              */
50680             "selectionchange" : true,
50681         /**
50682              * @event afterselectionchange
50683              * Fires after the selection changes (eg. by key press or clicking)
50684              * @param {SelectionModel} this
50685              */
50686             "afterselectionchange" : true,
50687         /**
50688              * @event beforerowselect
50689              * Fires when a row is selected being selected, return false to cancel.
50690              * @param {SelectionModel} this
50691              * @param {Number} rowIndex The selected index
50692              * @param {Boolean} keepExisting False if other selections will be cleared
50693              */
50694             "beforerowselect" : true,
50695         /**
50696              * @event rowselect
50697              * Fires when a row is selected.
50698              * @param {SelectionModel} this
50699              * @param {Number} rowIndex The selected index
50700              * @param {Roo.data.Record} r The record
50701              */
50702             "rowselect" : true,
50703         /**
50704              * @event rowdeselect
50705              * Fires when a row is deselected.
50706              * @param {SelectionModel} this
50707              * @param {Number} rowIndex The selected index
50708              */
50709         "rowdeselect" : true
50710     });
50711     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50712     this.locked = false;
50713 };
50714
50715 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50716     /**
50717      * @cfg {Boolean} singleSelect
50718      * True to allow selection of only one row at a time (defaults to false)
50719      */
50720     singleSelect : false,
50721
50722     // private
50723     initEvents : function(){
50724
50725         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50726             this.grid.on("mousedown", this.handleMouseDown, this);
50727         }else{ // allow click to work like normal
50728             this.grid.on("rowclick", this.handleDragableRowClick, this);
50729         }
50730
50731         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50732             "up" : function(e){
50733                 if(!e.shiftKey){
50734                     this.selectPrevious(e.shiftKey);
50735                 }else if(this.last !== false && this.lastActive !== false){
50736                     var last = this.last;
50737                     this.selectRange(this.last,  this.lastActive-1);
50738                     this.grid.getView().focusRow(this.lastActive);
50739                     if(last !== false){
50740                         this.last = last;
50741                     }
50742                 }else{
50743                     this.selectFirstRow();
50744                 }
50745                 this.fireEvent("afterselectionchange", this);
50746             },
50747             "down" : function(e){
50748                 if(!e.shiftKey){
50749                     this.selectNext(e.shiftKey);
50750                 }else if(this.last !== false && this.lastActive !== false){
50751                     var last = this.last;
50752                     this.selectRange(this.last,  this.lastActive+1);
50753                     this.grid.getView().focusRow(this.lastActive);
50754                     if(last !== false){
50755                         this.last = last;
50756                     }
50757                 }else{
50758                     this.selectFirstRow();
50759                 }
50760                 this.fireEvent("afterselectionchange", this);
50761             },
50762             scope: this
50763         });
50764
50765         var view = this.grid.view;
50766         view.on("refresh", this.onRefresh, this);
50767         view.on("rowupdated", this.onRowUpdated, this);
50768         view.on("rowremoved", this.onRemove, this);
50769     },
50770
50771     // private
50772     onRefresh : function(){
50773         var ds = this.grid.dataSource, i, v = this.grid.view;
50774         var s = this.selections;
50775         s.each(function(r){
50776             if((i = ds.indexOfId(r.id)) != -1){
50777                 v.onRowSelect(i);
50778             }else{
50779                 s.remove(r);
50780             }
50781         });
50782     },
50783
50784     // private
50785     onRemove : function(v, index, r){
50786         this.selections.remove(r);
50787     },
50788
50789     // private
50790     onRowUpdated : function(v, index, r){
50791         if(this.isSelected(r)){
50792             v.onRowSelect(index);
50793         }
50794     },
50795
50796     /**
50797      * Select records.
50798      * @param {Array} records The records to select
50799      * @param {Boolean} keepExisting (optional) True to keep existing selections
50800      */
50801     selectRecords : function(records, keepExisting){
50802         if(!keepExisting){
50803             this.clearSelections();
50804         }
50805         var ds = this.grid.dataSource;
50806         for(var i = 0, len = records.length; i < len; i++){
50807             this.selectRow(ds.indexOf(records[i]), true);
50808         }
50809     },
50810
50811     /**
50812      * Gets the number of selected rows.
50813      * @return {Number}
50814      */
50815     getCount : function(){
50816         return this.selections.length;
50817     },
50818
50819     /**
50820      * Selects the first row in the grid.
50821      */
50822     selectFirstRow : function(){
50823         this.selectRow(0);
50824     },
50825
50826     /**
50827      * Select the last row.
50828      * @param {Boolean} keepExisting (optional) True to keep existing selections
50829      */
50830     selectLastRow : function(keepExisting){
50831         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50832     },
50833
50834     /**
50835      * Selects the row immediately following the last selected row.
50836      * @param {Boolean} keepExisting (optional) True to keep existing selections
50837      */
50838     selectNext : function(keepExisting){
50839         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50840             this.selectRow(this.last+1, keepExisting);
50841             this.grid.getView().focusRow(this.last);
50842         }
50843     },
50844
50845     /**
50846      * Selects the row that precedes the last selected row.
50847      * @param {Boolean} keepExisting (optional) True to keep existing selections
50848      */
50849     selectPrevious : function(keepExisting){
50850         if(this.last){
50851             this.selectRow(this.last-1, keepExisting);
50852             this.grid.getView().focusRow(this.last);
50853         }
50854     },
50855
50856     /**
50857      * Returns the selected records
50858      * @return {Array} Array of selected records
50859      */
50860     getSelections : function(){
50861         return [].concat(this.selections.items);
50862     },
50863
50864     /**
50865      * Returns the first selected record.
50866      * @return {Record}
50867      */
50868     getSelected : function(){
50869         return this.selections.itemAt(0);
50870     },
50871
50872
50873     /**
50874      * Clears all selections.
50875      */
50876     clearSelections : function(fast){
50877         if(this.locked) return;
50878         if(fast !== true){
50879             var ds = this.grid.dataSource;
50880             var s = this.selections;
50881             s.each(function(r){
50882                 this.deselectRow(ds.indexOfId(r.id));
50883             }, this);
50884             s.clear();
50885         }else{
50886             this.selections.clear();
50887         }
50888         this.last = false;
50889     },
50890
50891
50892     /**
50893      * Selects all rows.
50894      */
50895     selectAll : function(){
50896         if(this.locked) return;
50897         this.selections.clear();
50898         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50899             this.selectRow(i, true);
50900         }
50901     },
50902
50903     /**
50904      * Returns True if there is a selection.
50905      * @return {Boolean}
50906      */
50907     hasSelection : function(){
50908         return this.selections.length > 0;
50909     },
50910
50911     /**
50912      * Returns True if the specified row is selected.
50913      * @param {Number/Record} record The record or index of the record to check
50914      * @return {Boolean}
50915      */
50916     isSelected : function(index){
50917         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50918         return (r && this.selections.key(r.id) ? true : false);
50919     },
50920
50921     /**
50922      * Returns True if the specified record id is selected.
50923      * @param {String} id The id of record to check
50924      * @return {Boolean}
50925      */
50926     isIdSelected : function(id){
50927         return (this.selections.key(id) ? true : false);
50928     },
50929
50930     // private
50931     handleMouseDown : function(e, t){
50932         var view = this.grid.getView(), rowIndex;
50933         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50934             return;
50935         };
50936         if(e.shiftKey && this.last !== false){
50937             var last = this.last;
50938             this.selectRange(last, rowIndex, e.ctrlKey);
50939             this.last = last; // reset the last
50940             view.focusRow(rowIndex);
50941         }else{
50942             var isSelected = this.isSelected(rowIndex);
50943             if(e.button !== 0 && isSelected){
50944                 view.focusRow(rowIndex);
50945             }else if(e.ctrlKey && isSelected){
50946                 this.deselectRow(rowIndex);
50947             }else if(!isSelected){
50948                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50949                 view.focusRow(rowIndex);
50950             }
50951         }
50952         this.fireEvent("afterselectionchange", this);
50953     },
50954     // private
50955     handleDragableRowClick :  function(grid, rowIndex, e) 
50956     {
50957         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50958             this.selectRow(rowIndex, false);
50959             grid.view.focusRow(rowIndex);
50960              this.fireEvent("afterselectionchange", this);
50961         }
50962     },
50963     
50964     /**
50965      * Selects multiple rows.
50966      * @param {Array} rows Array of the indexes of the row to select
50967      * @param {Boolean} keepExisting (optional) True to keep existing selections
50968      */
50969     selectRows : function(rows, keepExisting){
50970         if(!keepExisting){
50971             this.clearSelections();
50972         }
50973         for(var i = 0, len = rows.length; i < len; i++){
50974             this.selectRow(rows[i], true);
50975         }
50976     },
50977
50978     /**
50979      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50980      * @param {Number} startRow The index of the first row in the range
50981      * @param {Number} endRow The index of the last row in the range
50982      * @param {Boolean} keepExisting (optional) True to retain existing selections
50983      */
50984     selectRange : function(startRow, endRow, keepExisting){
50985         if(this.locked) return;
50986         if(!keepExisting){
50987             this.clearSelections();
50988         }
50989         if(startRow <= endRow){
50990             for(var i = startRow; i <= endRow; i++){
50991                 this.selectRow(i, true);
50992             }
50993         }else{
50994             for(var i = startRow; i >= endRow; i--){
50995                 this.selectRow(i, true);
50996             }
50997         }
50998     },
50999
51000     /**
51001      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51002      * @param {Number} startRow The index of the first row in the range
51003      * @param {Number} endRow The index of the last row in the range
51004      */
51005     deselectRange : function(startRow, endRow, preventViewNotify){
51006         if(this.locked) return;
51007         for(var i = startRow; i <= endRow; i++){
51008             this.deselectRow(i, preventViewNotify);
51009         }
51010     },
51011
51012     /**
51013      * Selects a row.
51014      * @param {Number} row The index of the row to select
51015      * @param {Boolean} keepExisting (optional) True to keep existing selections
51016      */
51017     selectRow : function(index, keepExisting, preventViewNotify){
51018         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51019         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51020             if(!keepExisting || this.singleSelect){
51021                 this.clearSelections();
51022             }
51023             var r = this.grid.dataSource.getAt(index);
51024             this.selections.add(r);
51025             this.last = this.lastActive = index;
51026             if(!preventViewNotify){
51027                 this.grid.getView().onRowSelect(index);
51028             }
51029             this.fireEvent("rowselect", this, index, r);
51030             this.fireEvent("selectionchange", this);
51031         }
51032     },
51033
51034     /**
51035      * Deselects a row.
51036      * @param {Number} row The index of the row to deselect
51037      */
51038     deselectRow : function(index, preventViewNotify){
51039         if(this.locked) return;
51040         if(this.last == index){
51041             this.last = false;
51042         }
51043         if(this.lastActive == index){
51044             this.lastActive = false;
51045         }
51046         var r = this.grid.dataSource.getAt(index);
51047         this.selections.remove(r);
51048         if(!preventViewNotify){
51049             this.grid.getView().onRowDeselect(index);
51050         }
51051         this.fireEvent("rowdeselect", this, index);
51052         this.fireEvent("selectionchange", this);
51053     },
51054
51055     // private
51056     restoreLast : function(){
51057         if(this._last){
51058             this.last = this._last;
51059         }
51060     },
51061
51062     // private
51063     acceptsNav : function(row, col, cm){
51064         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51065     },
51066
51067     // private
51068     onEditorKey : function(field, e){
51069         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51070         if(k == e.TAB){
51071             e.stopEvent();
51072             ed.completeEdit();
51073             if(e.shiftKey){
51074                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51075             }else{
51076                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51077             }
51078         }else if(k == e.ENTER && !e.ctrlKey){
51079             e.stopEvent();
51080             ed.completeEdit();
51081             if(e.shiftKey){
51082                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51083             }else{
51084                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51085             }
51086         }else if(k == e.ESC){
51087             ed.cancelEdit();
51088         }
51089         if(newCell){
51090             g.startEditing(newCell[0], newCell[1]);
51091         }
51092     }
51093 });/*
51094  * Based on:
51095  * Ext JS Library 1.1.1
51096  * Copyright(c) 2006-2007, Ext JS, LLC.
51097  *
51098  * Originally Released Under LGPL - original licence link has changed is not relivant.
51099  *
51100  * Fork - LGPL
51101  * <script type="text/javascript">
51102  */
51103 /**
51104  * @class Roo.grid.CellSelectionModel
51105  * @extends Roo.grid.AbstractSelectionModel
51106  * This class provides the basic implementation for cell selection in a grid.
51107  * @constructor
51108  * @param {Object} config The object containing the configuration of this model.
51109  */
51110 Roo.grid.CellSelectionModel = function(config){
51111     Roo.apply(this, config);
51112
51113     this.selection = null;
51114
51115     this.addEvents({
51116         /**
51117              * @event beforerowselect
51118              * Fires before 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             "beforecellselect" : true,
51124         /**
51125              * @event cellselect
51126              * Fires when a cell is selected.
51127              * @param {SelectionModel} this
51128              * @param {Number} rowIndex The selected row index
51129              * @param {Number} colIndex The selected cell index
51130              */
51131             "cellselect" : true,
51132         /**
51133              * @event selectionchange
51134              * Fires when the active selection changes.
51135              * @param {SelectionModel} this
51136              * @param {Object} selection null for no selection or an object (o) with two properties
51137                 <ul>
51138                 <li>o.record: the record object for the row the selection is in</li>
51139                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51140                 </ul>
51141              */
51142             "selectionchange" : true
51143     });
51144     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51145 };
51146
51147 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51148
51149     /** @ignore */
51150     initEvents : function(){
51151         this.grid.on("mousedown", this.handleMouseDown, this);
51152         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51153         var view = this.grid.view;
51154         view.on("refresh", this.onViewChange, this);
51155         view.on("rowupdated", this.onRowUpdated, this);
51156         view.on("beforerowremoved", this.clearSelections, this);
51157         view.on("beforerowsinserted", this.clearSelections, this);
51158         if(this.grid.isEditor){
51159             this.grid.on("beforeedit", this.beforeEdit,  this);
51160         }
51161     },
51162
51163         //private
51164     beforeEdit : function(e){
51165         this.select(e.row, e.column, false, true, e.record);
51166     },
51167
51168         //private
51169     onRowUpdated : function(v, index, r){
51170         if(this.selection && this.selection.record == r){
51171             v.onCellSelect(index, this.selection.cell[1]);
51172         }
51173     },
51174
51175         //private
51176     onViewChange : function(){
51177         this.clearSelections(true);
51178     },
51179
51180         /**
51181          * Returns the currently selected cell,.
51182          * @return {Array} The selected cell (row, column) or null if none selected.
51183          */
51184     getSelectedCell : function(){
51185         return this.selection ? this.selection.cell : null;
51186     },
51187
51188     /**
51189      * Clears all selections.
51190      * @param {Boolean} true to prevent the gridview from being notified about the change.
51191      */
51192     clearSelections : function(preventNotify){
51193         var s = this.selection;
51194         if(s){
51195             if(preventNotify !== true){
51196                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51197             }
51198             this.selection = null;
51199             this.fireEvent("selectionchange", this, null);
51200         }
51201     },
51202
51203     /**
51204      * Returns true if there is a selection.
51205      * @return {Boolean}
51206      */
51207     hasSelection : function(){
51208         return this.selection ? true : false;
51209     },
51210
51211     /** @ignore */
51212     handleMouseDown : function(e, t){
51213         var v = this.grid.getView();
51214         if(this.isLocked()){
51215             return;
51216         };
51217         var row = v.findRowIndex(t);
51218         var cell = v.findCellIndex(t);
51219         if(row !== false && cell !== false){
51220             this.select(row, cell);
51221         }
51222     },
51223
51224     /**
51225      * Selects a cell.
51226      * @param {Number} rowIndex
51227      * @param {Number} collIndex
51228      */
51229     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51230         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51231             this.clearSelections();
51232             r = r || this.grid.dataSource.getAt(rowIndex);
51233             this.selection = {
51234                 record : r,
51235                 cell : [rowIndex, colIndex]
51236             };
51237             if(!preventViewNotify){
51238                 var v = this.grid.getView();
51239                 v.onCellSelect(rowIndex, colIndex);
51240                 if(preventFocus !== true){
51241                     v.focusCell(rowIndex, colIndex);
51242                 }
51243             }
51244             this.fireEvent("cellselect", this, rowIndex, colIndex);
51245             this.fireEvent("selectionchange", this, this.selection);
51246         }
51247     },
51248
51249         //private
51250     isSelectable : function(rowIndex, colIndex, cm){
51251         return !cm.isHidden(colIndex);
51252     },
51253
51254     /** @ignore */
51255     handleKeyDown : function(e){
51256         //Roo.log('Cell Sel Model handleKeyDown');
51257         if(!e.isNavKeyPress()){
51258             return;
51259         }
51260         var g = this.grid, s = this.selection;
51261         if(!s){
51262             e.stopEvent();
51263             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51264             if(cell){
51265                 this.select(cell[0], cell[1]);
51266             }
51267             return;
51268         }
51269         var sm = this;
51270         var walk = function(row, col, step){
51271             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51272         };
51273         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51274         var newCell;
51275
51276         switch(k){
51277             case e.TAB:
51278                 // handled by onEditorKey
51279                 if (g.isEditor && g.editing) {
51280                     return;
51281                 }
51282                 if(e.shiftKey){
51283                      newCell = walk(r, c-1, -1);
51284                 }else{
51285                      newCell = walk(r, c+1, 1);
51286                 }
51287              break;
51288              case e.DOWN:
51289                  newCell = walk(r+1, c, 1);
51290              break;
51291              case e.UP:
51292                  newCell = walk(r-1, c, -1);
51293              break;
51294              case e.RIGHT:
51295                  newCell = walk(r, c+1, 1);
51296              break;
51297              case e.LEFT:
51298                  newCell = walk(r, c-1, -1);
51299              break;
51300              case e.ENTER:
51301                  if(g.isEditor && !g.editing){
51302                     g.startEditing(r, c);
51303                     e.stopEvent();
51304                     return;
51305                 }
51306              break;
51307         };
51308         if(newCell){
51309             this.select(newCell[0], newCell[1]);
51310             e.stopEvent();
51311         }
51312     },
51313
51314     acceptsNav : function(row, col, cm){
51315         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51316     },
51317     /**
51318      * Selects a cell.
51319      * @param {Number} field (not used) - as it's normally used as a listener
51320      * @param {Number} e - event - fake it by using
51321      *
51322      * var e = Roo.EventObjectImpl.prototype;
51323      * e.keyCode = e.TAB
51324      *
51325      * 
51326      */
51327     onEditorKey : function(field, e){
51328         
51329         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51330         ///Roo.log('onEditorKey' + k);
51331         if (!ed) {
51332             
51333             
51334             
51335         }
51336         if(k == e.TAB){
51337             if(e.shiftKey){
51338                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51339             }else{
51340                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51341             }
51342             
51343             e.stopEvent();
51344             
51345         }else if(k == e.ENTER &&  !e.ctrlKey){
51346             ed.completeEdit();
51347             e.stopEvent();
51348             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51349         }else if(k == e.ESC){
51350             ed.cancelEdit();
51351         }
51352         
51353         
51354         if(newCell){
51355             //Roo.log('next cell after edit');
51356             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51357         }
51358     }
51359 });/*
51360  * Based on:
51361  * Ext JS Library 1.1.1
51362  * Copyright(c) 2006-2007, Ext JS, LLC.
51363  *
51364  * Originally Released Under LGPL - original licence link has changed is not relivant.
51365  *
51366  * Fork - LGPL
51367  * <script type="text/javascript">
51368  */
51369  
51370 /**
51371  * @class Roo.grid.EditorGrid
51372  * @extends Roo.grid.Grid
51373  * Class for creating and editable grid.
51374  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51375  * The container MUST have some type of size defined for the grid to fill. The container will be 
51376  * automatically set to position relative if it isn't already.
51377  * @param {Object} dataSource The data model to bind to
51378  * @param {Object} colModel The column model with info about this grid's columns
51379  */
51380 Roo.grid.EditorGrid = function(container, config){
51381     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51382     this.getGridEl().addClass("xedit-grid");
51383
51384     if(!this.selModel){
51385         this.selModel = new Roo.grid.CellSelectionModel();
51386     }
51387
51388     this.activeEditor = null;
51389
51390         this.addEvents({
51391             /**
51392              * @event beforeedit
51393              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51394              * <ul style="padding:5px;padding-left:16px;">
51395              * <li>grid - This grid</li>
51396              * <li>record - The record being edited</li>
51397              * <li>field - The field name being edited</li>
51398              * <li>value - The value for the field being edited.</li>
51399              * <li>row - The grid row index</li>
51400              * <li>column - The grid column index</li>
51401              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51402              * </ul>
51403              * @param {Object} e An edit event (see above for description)
51404              */
51405             "beforeedit" : true,
51406             /**
51407              * @event afteredit
51408              * Fires after a cell is edited. <br />
51409              * <ul style="padding:5px;padding-left:16px;">
51410              * <li>grid - This grid</li>
51411              * <li>record - The record being edited</li>
51412              * <li>field - The field name being edited</li>
51413              * <li>value - The value being set</li>
51414              * <li>originalValue - The original value for the field, before the edit.</li>
51415              * <li>row - The grid row index</li>
51416              * <li>column - The grid column index</li>
51417              * </ul>
51418              * @param {Object} e An edit event (see above for description)
51419              */
51420             "afteredit" : true,
51421             /**
51422              * @event validateedit
51423              * Fires after a cell is edited, but before the value is set in the record. 
51424          * You can use this to modify the value being set in the field, Return false
51425              * to cancel the change. The edit event object has the following properties <br />
51426              * <ul style="padding:5px;padding-left:16px;">
51427          * <li>editor - This editor</li>
51428              * <li>grid - This grid</li>
51429              * <li>record - The record being edited</li>
51430              * <li>field - The field name being edited</li>
51431              * <li>value - The value being set</li>
51432              * <li>originalValue - The original value for the field, before the edit.</li>
51433              * <li>row - The grid row index</li>
51434              * <li>column - The grid column index</li>
51435              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51436              * </ul>
51437              * @param {Object} e An edit event (see above for description)
51438              */
51439             "validateedit" : true
51440         });
51441     this.on("bodyscroll", this.stopEditing,  this);
51442     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51443 };
51444
51445 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51446     /**
51447      * @cfg {Number} clicksToEdit
51448      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51449      */
51450     clicksToEdit: 2,
51451
51452     // private
51453     isEditor : true,
51454     // private
51455     trackMouseOver: false, // causes very odd FF errors
51456
51457     onCellDblClick : function(g, row, col){
51458         this.startEditing(row, col);
51459     },
51460
51461     onEditComplete : function(ed, value, startValue){
51462         this.editing = false;
51463         this.activeEditor = null;
51464         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51465         var r = ed.record;
51466         var field = this.colModel.getDataIndex(ed.col);
51467         var e = {
51468             grid: this,
51469             record: r,
51470             field: field,
51471             originalValue: startValue,
51472             value: value,
51473             row: ed.row,
51474             column: ed.col,
51475             cancel:false,
51476             editor: ed
51477         };
51478         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51479         cell.show();
51480           
51481         if(String(value) !== String(startValue)){
51482             
51483             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51484                 r.set(field, e.value);
51485                 // if we are dealing with a combo box..
51486                 // then we also set the 'name' colum to be the displayField
51487                 if (ed.field.displayField && ed.field.name) {
51488                     r.set(ed.field.name, ed.field.el.dom.value);
51489                 }
51490                 
51491                 delete e.cancel; //?? why!!!
51492                 this.fireEvent("afteredit", e);
51493             }
51494         } else {
51495             this.fireEvent("afteredit", e); // always fire it!
51496         }
51497         this.view.focusCell(ed.row, ed.col);
51498     },
51499
51500     /**
51501      * Starts editing the specified for the specified row/column
51502      * @param {Number} rowIndex
51503      * @param {Number} colIndex
51504      */
51505     startEditing : function(row, col){
51506         this.stopEditing();
51507         if(this.colModel.isCellEditable(col, row)){
51508             this.view.ensureVisible(row, col, true);
51509           
51510             var r = this.dataSource.getAt(row);
51511             var field = this.colModel.getDataIndex(col);
51512             var cell = Roo.get(this.view.getCell(row,col));
51513             var e = {
51514                 grid: this,
51515                 record: r,
51516                 field: field,
51517                 value: r.data[field],
51518                 row: row,
51519                 column: col,
51520                 cancel:false 
51521             };
51522             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51523                 this.editing = true;
51524                 var ed = this.colModel.getCellEditor(col, row);
51525                 
51526                 if (!ed) {
51527                     return;
51528                 }
51529                 if(!ed.rendered){
51530                     ed.render(ed.parentEl || document.body);
51531                 }
51532                 ed.field.reset();
51533                
51534                 cell.hide();
51535                 
51536                 (function(){ // complex but required for focus issues in safari, ie and opera
51537                     ed.row = row;
51538                     ed.col = col;
51539                     ed.record = r;
51540                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51541                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51542                     this.activeEditor = ed;
51543                     var v = r.data[field];
51544                     ed.startEdit(this.view.getCell(row, col), v);
51545                     // combo's with 'displayField and name set
51546                     if (ed.field.displayField && ed.field.name) {
51547                         ed.field.el.dom.value = r.data[ed.field.name];
51548                     }
51549                     
51550                     
51551                 }).defer(50, this);
51552             }
51553         }
51554     },
51555         
51556     /**
51557      * Stops any active editing
51558      */
51559     stopEditing : function(){
51560         if(this.activeEditor){
51561             this.activeEditor.completeEdit();
51562         }
51563         this.activeEditor = null;
51564     }
51565 });/*
51566  * Based on:
51567  * Ext JS Library 1.1.1
51568  * Copyright(c) 2006-2007, Ext JS, LLC.
51569  *
51570  * Originally Released Under LGPL - original licence link has changed is not relivant.
51571  *
51572  * Fork - LGPL
51573  * <script type="text/javascript">
51574  */
51575
51576 // private - not really -- you end up using it !
51577 // This is a support class used internally by the Grid components
51578
51579 /**
51580  * @class Roo.grid.GridEditor
51581  * @extends Roo.Editor
51582  * Class for creating and editable grid elements.
51583  * @param {Object} config any settings (must include field)
51584  */
51585 Roo.grid.GridEditor = function(field, config){
51586     if (!config && field.field) {
51587         config = field;
51588         field = Roo.factory(config.field, Roo.form);
51589     }
51590     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51591     field.monitorTab = false;
51592 };
51593
51594 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51595     
51596     /**
51597      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51598      */
51599     
51600     alignment: "tl-tl",
51601     autoSize: "width",
51602     hideEl : false,
51603     cls: "x-small-editor x-grid-editor",
51604     shim:false,
51605     shadow:"frame"
51606 });/*
51607  * Based on:
51608  * Ext JS Library 1.1.1
51609  * Copyright(c) 2006-2007, Ext JS, LLC.
51610  *
51611  * Originally Released Under LGPL - original licence link has changed is not relivant.
51612  *
51613  * Fork - LGPL
51614  * <script type="text/javascript">
51615  */
51616   
51617
51618   
51619 Roo.grid.PropertyRecord = Roo.data.Record.create([
51620     {name:'name',type:'string'},  'value'
51621 ]);
51622
51623
51624 Roo.grid.PropertyStore = function(grid, source){
51625     this.grid = grid;
51626     this.store = new Roo.data.Store({
51627         recordType : Roo.grid.PropertyRecord
51628     });
51629     this.store.on('update', this.onUpdate,  this);
51630     if(source){
51631         this.setSource(source);
51632     }
51633     Roo.grid.PropertyStore.superclass.constructor.call(this);
51634 };
51635
51636
51637
51638 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51639     setSource : function(o){
51640         this.source = o;
51641         this.store.removeAll();
51642         var data = [];
51643         for(var k in o){
51644             if(this.isEditableValue(o[k])){
51645                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51646             }
51647         }
51648         this.store.loadRecords({records: data}, {}, true);
51649     },
51650
51651     onUpdate : function(ds, record, type){
51652         if(type == Roo.data.Record.EDIT){
51653             var v = record.data['value'];
51654             var oldValue = record.modified['value'];
51655             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51656                 this.source[record.id] = v;
51657                 record.commit();
51658                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51659             }else{
51660                 record.reject();
51661             }
51662         }
51663     },
51664
51665     getProperty : function(row){
51666        return this.store.getAt(row);
51667     },
51668
51669     isEditableValue: function(val){
51670         if(val && val instanceof Date){
51671             return true;
51672         }else if(typeof val == 'object' || typeof val == 'function'){
51673             return false;
51674         }
51675         return true;
51676     },
51677
51678     setValue : function(prop, value){
51679         this.source[prop] = value;
51680         this.store.getById(prop).set('value', value);
51681     },
51682
51683     getSource : function(){
51684         return this.source;
51685     }
51686 });
51687
51688 Roo.grid.PropertyColumnModel = function(grid, store){
51689     this.grid = grid;
51690     var g = Roo.grid;
51691     g.PropertyColumnModel.superclass.constructor.call(this, [
51692         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51693         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51694     ]);
51695     this.store = store;
51696     this.bselect = Roo.DomHelper.append(document.body, {
51697         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51698             {tag: 'option', value: 'true', html: 'true'},
51699             {tag: 'option', value: 'false', html: 'false'}
51700         ]
51701     });
51702     Roo.id(this.bselect);
51703     var f = Roo.form;
51704     this.editors = {
51705         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51706         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51707         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51708         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51709         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51710     };
51711     this.renderCellDelegate = this.renderCell.createDelegate(this);
51712     this.renderPropDelegate = this.renderProp.createDelegate(this);
51713 };
51714
51715 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51716     
51717     
51718     nameText : 'Name',
51719     valueText : 'Value',
51720     
51721     dateFormat : 'm/j/Y',
51722     
51723     
51724     renderDate : function(dateVal){
51725         return dateVal.dateFormat(this.dateFormat);
51726     },
51727
51728     renderBool : function(bVal){
51729         return bVal ? 'true' : 'false';
51730     },
51731
51732     isCellEditable : function(colIndex, rowIndex){
51733         return colIndex == 1;
51734     },
51735
51736     getRenderer : function(col){
51737         return col == 1 ?
51738             this.renderCellDelegate : this.renderPropDelegate;
51739     },
51740
51741     renderProp : function(v){
51742         return this.getPropertyName(v);
51743     },
51744
51745     renderCell : function(val){
51746         var rv = val;
51747         if(val instanceof Date){
51748             rv = this.renderDate(val);
51749         }else if(typeof val == 'boolean'){
51750             rv = this.renderBool(val);
51751         }
51752         return Roo.util.Format.htmlEncode(rv);
51753     },
51754
51755     getPropertyName : function(name){
51756         var pn = this.grid.propertyNames;
51757         return pn && pn[name] ? pn[name] : name;
51758     },
51759
51760     getCellEditor : function(colIndex, rowIndex){
51761         var p = this.store.getProperty(rowIndex);
51762         var n = p.data['name'], val = p.data['value'];
51763         
51764         if(typeof(this.grid.customEditors[n]) == 'string'){
51765             return this.editors[this.grid.customEditors[n]];
51766         }
51767         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51768             return this.grid.customEditors[n];
51769         }
51770         if(val instanceof Date){
51771             return this.editors['date'];
51772         }else if(typeof val == 'number'){
51773             return this.editors['number'];
51774         }else if(typeof val == 'boolean'){
51775             return this.editors['boolean'];
51776         }else{
51777             return this.editors['string'];
51778         }
51779     }
51780 });
51781
51782 /**
51783  * @class Roo.grid.PropertyGrid
51784  * @extends Roo.grid.EditorGrid
51785  * This class represents the  interface of a component based property grid control.
51786  * <br><br>Usage:<pre><code>
51787  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51788       
51789  });
51790  // set any options
51791  grid.render();
51792  * </code></pre>
51793   
51794  * @constructor
51795  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51796  * The container MUST have some type of size defined for the grid to fill. The container will be
51797  * automatically set to position relative if it isn't already.
51798  * @param {Object} config A config object that sets properties on this grid.
51799  */
51800 Roo.grid.PropertyGrid = function(container, config){
51801     config = config || {};
51802     var store = new Roo.grid.PropertyStore(this);
51803     this.store = store;
51804     var cm = new Roo.grid.PropertyColumnModel(this, store);
51805     store.store.sort('name', 'ASC');
51806     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51807         ds: store.store,
51808         cm: cm,
51809         enableColLock:false,
51810         enableColumnMove:false,
51811         stripeRows:false,
51812         trackMouseOver: false,
51813         clicksToEdit:1
51814     }, config));
51815     this.getGridEl().addClass('x-props-grid');
51816     this.lastEditRow = null;
51817     this.on('columnresize', this.onColumnResize, this);
51818     this.addEvents({
51819          /**
51820              * @event beforepropertychange
51821              * Fires before a property changes (return false to stop?)
51822              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51823              * @param {String} id Record Id
51824              * @param {String} newval New Value
51825          * @param {String} oldval Old Value
51826              */
51827         "beforepropertychange": true,
51828         /**
51829              * @event propertychange
51830              * Fires after a property changes
51831              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51832              * @param {String} id Record Id
51833              * @param {String} newval New Value
51834          * @param {String} oldval Old Value
51835              */
51836         "propertychange": true
51837     });
51838     this.customEditors = this.customEditors || {};
51839 };
51840 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51841     
51842      /**
51843      * @cfg {Object} customEditors map of colnames=> custom editors.
51844      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51845      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51846      * false disables editing of the field.
51847          */
51848     
51849       /**
51850      * @cfg {Object} propertyNames map of property Names to their displayed value
51851          */
51852     
51853     render : function(){
51854         Roo.grid.PropertyGrid.superclass.render.call(this);
51855         this.autoSize.defer(100, this);
51856     },
51857
51858     autoSize : function(){
51859         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51860         if(this.view){
51861             this.view.fitColumns();
51862         }
51863     },
51864
51865     onColumnResize : function(){
51866         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51867         this.autoSize();
51868     },
51869     /**
51870      * Sets the data for the Grid
51871      * accepts a Key => Value object of all the elements avaiable.
51872      * @param {Object} data  to appear in grid.
51873      */
51874     setSource : function(source){
51875         this.store.setSource(source);
51876         //this.autoSize();
51877     },
51878     /**
51879      * Gets all the data from the grid.
51880      * @return {Object} data  data stored in grid
51881      */
51882     getSource : function(){
51883         return this.store.getSource();
51884     }
51885 });/*
51886  * Based on:
51887  * Ext JS Library 1.1.1
51888  * Copyright(c) 2006-2007, Ext JS, LLC.
51889  *
51890  * Originally Released Under LGPL - original licence link has changed is not relivant.
51891  *
51892  * Fork - LGPL
51893  * <script type="text/javascript">
51894  */
51895  
51896 /**
51897  * @class Roo.LoadMask
51898  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51899  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51900  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51901  * element's UpdateManager load indicator and will be destroyed after the initial load.
51902  * @constructor
51903  * Create a new LoadMask
51904  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51905  * @param {Object} config The config object
51906  */
51907 Roo.LoadMask = function(el, config){
51908     this.el = Roo.get(el);
51909     Roo.apply(this, config);
51910     if(this.store){
51911         this.store.on('beforeload', this.onBeforeLoad, this);
51912         this.store.on('load', this.onLoad, this);
51913         this.store.on('loadexception', this.onLoad, this);
51914         this.removeMask = false;
51915     }else{
51916         var um = this.el.getUpdateManager();
51917         um.showLoadIndicator = false; // disable the default indicator
51918         um.on('beforeupdate', this.onBeforeLoad, this);
51919         um.on('update', this.onLoad, this);
51920         um.on('failure', this.onLoad, this);
51921         this.removeMask = true;
51922     }
51923 };
51924
51925 Roo.LoadMask.prototype = {
51926     /**
51927      * @cfg {Boolean} removeMask
51928      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51929      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51930      */
51931     /**
51932      * @cfg {String} msg
51933      * The text to display in a centered loading message box (defaults to 'Loading...')
51934      */
51935     msg : 'Loading...',
51936     /**
51937      * @cfg {String} msgCls
51938      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51939      */
51940     msgCls : 'x-mask-loading',
51941
51942     /**
51943      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51944      * @type Boolean
51945      */
51946     disabled: false,
51947
51948     /**
51949      * Disables the mask to prevent it from being displayed
51950      */
51951     disable : function(){
51952        this.disabled = true;
51953     },
51954
51955     /**
51956      * Enables the mask so that it can be displayed
51957      */
51958     enable : function(){
51959         this.disabled = false;
51960     },
51961
51962     // private
51963     onLoad : function(){
51964         this.el.unmask(this.removeMask);
51965     },
51966
51967     // private
51968     onBeforeLoad : function(){
51969         if(!this.disabled){
51970             this.el.mask(this.msg, this.msgCls);
51971         }
51972     },
51973
51974     // private
51975     destroy : function(){
51976         if(this.store){
51977             this.store.un('beforeload', this.onBeforeLoad, this);
51978             this.store.un('load', this.onLoad, this);
51979             this.store.un('loadexception', this.onLoad, this);
51980         }else{
51981             var um = this.el.getUpdateManager();
51982             um.un('beforeupdate', this.onBeforeLoad, this);
51983             um.un('update', this.onLoad, this);
51984             um.un('failure', this.onLoad, this);
51985         }
51986     }
51987 };/*
51988  * Based on:
51989  * Ext JS Library 1.1.1
51990  * Copyright(c) 2006-2007, Ext JS, LLC.
51991  *
51992  * Originally Released Under LGPL - original licence link has changed is not relivant.
51993  *
51994  * Fork - LGPL
51995  * <script type="text/javascript">
51996  */
51997 Roo.XTemplate = function(){
51998     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51999     var s = this.html;
52000
52001     s = ['<tpl>', s, '</tpl>'].join('');
52002
52003     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52004
52005     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52006     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52007     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52008     var m, id = 0;
52009     var tpls = [];
52010
52011     while(m = s.match(re)){
52012        var m2 = m[0].match(nameRe);
52013        var m3 = m[0].match(ifRe);
52014        var m4 = m[0].match(execRe);
52015        var exp = null, fn = null, exec = null;
52016        var name = m2 && m2[1] ? m2[1] : '';
52017        if(m3){
52018            exp = m3 && m3[1] ? m3[1] : null;
52019            if(exp){
52020                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52021            }
52022        }
52023        if(m4){
52024            exp = m4 && m4[1] ? m4[1] : null;
52025            if(exp){
52026                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52027            }
52028        }
52029        if(name){
52030            switch(name){
52031                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52032                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52033                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52034            }
52035        }
52036        tpls.push({
52037             id: id,
52038             target: name,
52039             exec: exec,
52040             test: fn,
52041             body: m[1]||''
52042         });
52043        s = s.replace(m[0], '{xtpl'+ id + '}');
52044        ++id;
52045     }
52046     for(var i = tpls.length-1; i >= 0; --i){
52047         this.compileTpl(tpls[i]);
52048     }
52049     this.master = tpls[tpls.length-1];
52050     this.tpls = tpls;
52051 };
52052 Roo.extend(Roo.XTemplate, Roo.Template, {
52053
52054     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52055
52056     applySubTemplate : function(id, values, parent){
52057         var t = this.tpls[id];
52058         if(t.test && !t.test.call(this, values, parent)){
52059             return '';
52060         }
52061         if(t.exec && t.exec.call(this, values, parent)){
52062             return '';
52063         }
52064         var vs = t.target ? t.target.call(this, values, parent) : values;
52065         parent = t.target ? values : parent;
52066         if(t.target && vs instanceof Array){
52067             var buf = [];
52068             for(var i = 0, len = vs.length; i < len; i++){
52069                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52070             }
52071             return buf.join('');
52072         }
52073         return t.compiled.call(this, vs, parent);
52074     },
52075
52076     compileTpl : function(tpl){
52077         var fm = Roo.util.Format;
52078         var useF = this.disableFormats !== true;
52079         var sep = Roo.isGecko ? "+" : ",";
52080         var fn = function(m, name, format, args){
52081             if(name.substr(0, 4) == 'xtpl'){
52082                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52083             }
52084             var v;
52085             if(name.indexOf('.') != -1){
52086                 v = name;
52087             }else{
52088                 v = "values['" + name + "']";
52089             }
52090             if(format && useF){
52091                 args = args ? ',' + args : "";
52092                 if(format.substr(0, 5) != "this."){
52093                     format = "fm." + format + '(';
52094                 }else{
52095                     format = 'this.call("'+ format.substr(5) + '", ';
52096                     args = ", values";
52097                 }
52098             }else{
52099                 args= ''; format = "("+v+" === undefined ? '' : ";
52100             }
52101             return "'"+ sep + format + v + args + ")"+sep+"'";
52102         };
52103         var body;
52104         // branched to use + in gecko and [].join() in others
52105         if(Roo.isGecko){
52106             body = "tpl.compiled = function(values, parent){ return '" +
52107                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52108                     "';};";
52109         }else{
52110             body = ["tpl.compiled = function(values, parent){ return ['"];
52111             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52112             body.push("'].join('');};");
52113             body = body.join('');
52114         }
52115         /** eval:var:zzzzzzz */
52116         eval(body);
52117         return this;
52118     },
52119
52120     applyTemplate : function(values){
52121         return this.master.compiled.call(this, values, {});
52122         var s = this.subs;
52123     },
52124
52125     apply : function(){
52126         return this.applyTemplate.apply(this, arguments);
52127     },
52128
52129     compile : function(){return this;}
52130 });
52131
52132 Roo.XTemplate.from = function(el){
52133     el = Roo.getDom(el);
52134     return new Roo.XTemplate(el.value || el.innerHTML);
52135 };/*
52136  * Original code for Roojs - LGPL
52137  * <script type="text/javascript">
52138  */
52139  
52140 /**
52141  * @class Roo.XComponent
52142  * A delayed Element creator...
52143  * Or a way to group chunks of interface together.
52144  * 
52145  * Mypart.xyx = new Roo.XComponent({
52146
52147     parent : 'Mypart.xyz', // empty == document.element.!!
52148     order : '001',
52149     name : 'xxxx'
52150     region : 'xxxx'
52151     disabled : function() {} 
52152      
52153     tree : function() { // return an tree of xtype declared components
52154         var MODULE = this;
52155         return 
52156         {
52157             xtype : 'NestedLayoutPanel',
52158             // technicall
52159         }
52160      ]
52161  *})
52162  *
52163  *
52164  * It can be used to build a big heiracy, with parent etc.
52165  * or you can just use this to render a single compoent to a dom element
52166  * MYPART.render(Roo.Element | String(id) | dom_element )
52167  * 
52168  * @extends Roo.util.Observable
52169  * @constructor
52170  * @param cfg {Object} configuration of component
52171  * 
52172  */
52173 Roo.XComponent = function(cfg) {
52174     Roo.apply(this, cfg);
52175     this.addEvents({ 
52176         /**
52177              * @event built
52178              * Fires when this the componnt is built
52179              * @param {Roo.XComponent} c the component
52180              */
52181         'built' : true,
52182         /**
52183              * @event buildcomplete
52184              * Fires on the top level element when all elements have been built
52185              * @param {Roo.XComponent} c the top level component.
52186          */
52187         'buildcomplete' : true
52188         
52189     });
52190     this.region = this.region || 'center'; // default..
52191     Roo.XComponent.register(this);
52192     this.modules = false;
52193     this.el = false; // where the layout goes..
52194     
52195     
52196 }
52197 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52198     /**
52199      * @property el
52200      * The created element (with Roo.factory())
52201      * @type {Roo.Layout}
52202      */
52203     el  : false,
52204     
52205     /**
52206      * @property el
52207      * for BC  - use el in new code
52208      * @type {Roo.Layout}
52209      */
52210     panel : false,
52211     
52212     /**
52213      * @property layout
52214      * for BC  - use el in new code
52215      * @type {Roo.Layout}
52216      */
52217     layout : false,
52218     
52219      /**
52220      * @cfg {Function|boolean} disabled
52221      * If this module is disabled by some rule, return true from the funtion
52222      */
52223     disabled : false,
52224     
52225     /**
52226      * @cfg {String} parent 
52227      * Name of parent element which it get xtype added to..
52228      */
52229     parent: false,
52230     
52231     /**
52232      * @cfg {String} order
52233      * Used to set the order in which elements are created (usefull for multiple tabs)
52234      */
52235     
52236     order : false,
52237     /**
52238      * @cfg {String} name
52239      * String to display while loading.
52240      */
52241     name : false,
52242     /**
52243      * @cfg {String} region
52244      * Region to render component to (defaults to center)
52245      */
52246     region : 'center',
52247     
52248     /**
52249      * @cfg {Array} items
52250      * A single item array - the first element is the root of the tree..
52251      * It's done this way to stay compatible with the Xtype system...
52252      */
52253     items : false,
52254     
52255     
52256      /**
52257      * render
52258      * render element to dom or tree
52259      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52260      */
52261     
52262     render : function(el)
52263     {
52264         
52265         el = el || false;
52266         var hp = this.parent ? 1 : 0;
52267         
52268         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52269             // if parent is a '#.....' string, then let's use that..
52270             var ename = this.parent.substr(1)
52271             this.parent = false;
52272             el = Roo.get(ename);
52273             if (!el) {
52274                 Roo.log("Warning - element can not be found :#" + ename );
52275                 return;
52276             }
52277         }
52278         
52279         
52280         if (!this.parent) {
52281             
52282             el = el ? Roo.get(el) : false;
52283             
52284             // it's a top level one..
52285             this.parent =  {
52286                 el : new Roo.BorderLayout(el || document.body, {
52287                 
52288                      center: {
52289                          titlebar: false,
52290                          autoScroll:false,
52291                          closeOnTab: true,
52292                          tabPosition: 'top',
52293                           //resizeTabs: true,
52294                          alwaysShowTabs: el && hp? false :  true,
52295                          hideTabs: el || !hp ? true :  false,
52296                          minTabWidth: 140
52297                      }
52298                  })
52299             }
52300         }
52301         
52302         
52303             
52304         var tree = this.tree();
52305         tree.region = tree.region || this.region;
52306         this.el = this.parent.el.addxtype(tree);
52307         this.fireEvent('built', this);
52308         
52309         this.panel = this.el;
52310         this.layout = this.panel.layout;    
52311          
52312     }
52313     
52314 });
52315
52316 Roo.apply(Roo.XComponent, {
52317     
52318     /**
52319      * @property  buildCompleted
52320      * True when the builder has completed building the interface.
52321      * @type Boolean
52322      */
52323     buildCompleted : false,
52324      
52325     /**
52326      * @property  topModule
52327      * the upper most module - uses document.element as it's constructor.
52328      * @type Object
52329      */
52330      
52331     topModule  : false,
52332       
52333     /**
52334      * @property  modules
52335      * array of modules to be created by registration system.
52336      * @type {Array} of Roo.XComponent
52337      */
52338     
52339     modules : [],
52340     /**
52341      * @property  elmodules
52342      * array of modules to be created by which use #ID 
52343      * @type {Array} of Roo.XComponent
52344      */
52345      
52346     elmodules : [],
52347
52348     
52349     /**
52350      * Register components to be built later.
52351      *
52352      * This solves the following issues
52353      * - Building is not done on page load, but after an authentication process has occured.
52354      * - Interface elements are registered on page load
52355      * - Parent Interface elements may not be loaded before child, so this handles that..
52356      * 
52357      *
52358      * example:
52359      * 
52360      * MyApp.register({
52361           order : '000001',
52362           module : 'Pman.Tab.projectMgr',
52363           region : 'center',
52364           parent : 'Pman.layout',
52365           disabled : false,  // or use a function..
52366         })
52367      
52368      * * @param {Object} details about module
52369      */
52370     register : function(obj) {
52371         this.modules.push(obj);
52372          
52373     },
52374     /**
52375      * convert a string to an object..
52376      * eg. 'AAA.BBB' -> finds AAA.BBB
52377
52378      */
52379     
52380     toObject : function(str)
52381     {
52382         if (!str || typeof(str) == 'object') {
52383             return str;
52384         }
52385         if (str.substring(0,1) == '#') {
52386             return str;
52387         }
52388
52389         var ar = str.split('.');
52390         var rt, o;
52391         rt = ar.shift();
52392             /** eval:var:o */
52393         try {
52394             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52395         } catch (e) {
52396             throw "Module not found : " + str;
52397         }
52398         
52399         if (o === false) {
52400             throw "Module not found : " + str;
52401         }
52402         Roo.each(ar, function(e) {
52403             if (typeof(o[e]) == 'undefined') {
52404                 throw "Module not found : " + str;
52405             }
52406             o = o[e];
52407         });
52408         
52409         return o;
52410         
52411     },
52412     
52413     
52414     /**
52415      * move modules into their correct place in the tree..
52416      * 
52417      */
52418     preBuild : function ()
52419     {
52420         var _t = this;
52421         Roo.each(this.modules , function (obj)
52422         {
52423             var opar = obj.parent;
52424             try { 
52425                 obj.parent = this.toObject(opar);
52426             } catch(e) {
52427                 Roo.log(e.toString());
52428                 return;
52429             }
52430             
52431             if (!obj.parent) {
52432                 this.topModule = obj;
52433                 return;
52434             }
52435             if (typeof(obj.parent) == 'string') {
52436                 this.elmodules.push(obj);
52437                 return;
52438             }
52439             if (obj.parent.constructor != Roo.XComponent) {
52440                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52441             }
52442             if (!obj.parent.modules) {
52443                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52444                     function(o) { return o.order + '' }
52445                 );
52446             }
52447             
52448             obj.parent.modules.add(obj);
52449         }, this);
52450     },
52451     
52452      /**
52453      * make a list of modules to build.
52454      * @return {Array} list of modules. 
52455      */ 
52456     
52457     buildOrder : function()
52458     {
52459         var _this = this;
52460         var cmp = function(a,b) {   
52461             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52462         };
52463         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52464             throw "No top level modules to build";
52465         }
52466         
52467         // make a flat list in order of modules to build.
52468         var mods = this.topModule ? [ this.topModule ] : [];
52469         Roo.each(this.elmodules,function(e) { mods.push(e) });
52470
52471         
52472         // add modules to their parents..
52473         var addMod = function(m) {
52474            // Roo.debug && Roo.log(m.modKey);
52475             
52476             mods.push(m);
52477             if (m.modules) {
52478                 m.modules.keySort('ASC',  cmp );
52479                 m.modules.each(addMod);
52480             }
52481             // not sure if this is used any more..
52482             if (m.finalize) {
52483                 m.finalize.name = m.name + " (clean up) ";
52484                 mods.push(m.finalize);
52485             }
52486             
52487         }
52488         if (this.topModule) { 
52489             this.topModule.modules.keySort('ASC',  cmp );
52490             this.topModule.modules.each(addMod);
52491         }
52492         return mods;
52493     },
52494     
52495      /**
52496      * Build the registered modules.
52497      * @param {Object} parent element.
52498      * @param {Function} optional method to call after module has been added.
52499      * 
52500      */ 
52501    
52502     build : function() 
52503     {
52504         
52505         this.preBuild();
52506         var mods = this.buildOrder();
52507       
52508         //this.allmods = mods;
52509         //Roo.debug && Roo.log(mods);
52510         //return;
52511         if (!mods.length) { // should not happen
52512             throw "NO modules!!!";
52513         }
52514         
52515         
52516         
52517         // flash it up as modal - so we store the mask!?
52518         Roo.MessageBox.show({ title: 'loading' });
52519         Roo.MessageBox.show({
52520            title: "Please wait...",
52521            msg: "Building Interface...",
52522            width:450,
52523            progress:true,
52524            closable:false,
52525            modal: false
52526           
52527         });
52528         var total = mods.length;
52529         
52530         var _this = this;
52531         var progressRun = function() {
52532             if (!mods.length) {
52533                 Roo.debug && Roo.log('hide?');
52534                 Roo.MessageBox.hide();
52535                 if (_this.topModule) { 
52536                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52537                 }
52538                 // THE END...
52539                 return false;   
52540             }
52541             
52542             var m = mods.shift();
52543             
52544             
52545             Roo.debug && Roo.log(m);
52546             // not sure if this is supported any more.. - modules that are are just function
52547             if (typeof(m) == 'function') { 
52548                 m.call(this);
52549                 return progressRun.defer(10, _this);
52550             } 
52551             
52552             
52553             
52554             Roo.MessageBox.updateProgress(
52555                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52556                     " of " + total + 
52557                     (m.name ? (' - ' + m.name) : '')
52558                     );
52559             
52560          
52561             // is the module disabled?
52562             var disabled = (typeof(m.disabled) == 'function') ?
52563                 m.disabled.call(m.module.disabled) : m.disabled;    
52564             
52565             
52566             if (disabled) {
52567                 return progressRun(); // we do not update the display!
52568             }
52569             
52570             // now build 
52571             
52572             m.render();
52573             // it's 10 on top level, and 1 on others??? why...
52574             return progressRun.defer(10, _this);
52575              
52576         }
52577         progressRun.defer(1, _this);
52578      
52579         
52580         
52581     }
52582     
52583      
52584    
52585     
52586     
52587 });
52588  //<script type="text/javascript">
52589
52590
52591 /**
52592  * @class Roo.Login
52593  * @extends Roo.LayoutDialog
52594  * A generic Login Dialog..... - only one needed in theory!?!?
52595  *
52596  * Fires XComponent builder on success...
52597  * 
52598  * Sends 
52599  *    username,password, lang = for login actions.
52600  *    check = 1 for periodic checking that sesion is valid.
52601  *    passwordRequest = email request password
52602  *    logout = 1 = to logout
52603  * 
52604  * Affects: (this id="????" elements)
52605  *   loading  (removed) (used to indicate application is loading)
52606  *   loading-mask (hides) (used to hide application when it's building loading)
52607  *   
52608  * 
52609  * Usage: 
52610  *    
52611  * 
52612  * Myapp.login = Roo.Login({
52613      url: xxxx,
52614    
52615      realm : 'Myapp', 
52616      
52617      
52618      method : 'POST',
52619      
52620      
52621      * 
52622  })
52623  * 
52624  * 
52625  * 
52626  **/
52627  
52628 Roo.Login = function(cfg)
52629 {
52630     this.addEvents({
52631         'refreshed' : true
52632     });
52633     
52634     Roo.apply(this,cfg);
52635     
52636     Roo.onReady(function() {
52637         this.onLoad();
52638     }, this);
52639     // call parent..
52640     
52641    
52642     Roo.Login.superclass.constructor.call(this, this);
52643     //this.addxtype(this.items[0]);
52644     
52645     
52646 }
52647
52648
52649 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52650     
52651     /**
52652      * @cfg {String} method
52653      * Method used to query for login details.
52654      */
52655     
52656     method : 'POST',
52657     /**
52658      * @cfg {String} url
52659      * URL to query login data. - eg. baseURL + '/Login.php'
52660      */
52661     url : '',
52662     
52663     /**
52664      * @property user
52665      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52666      * @type {Object} 
52667      */
52668     user : false,
52669     /**
52670      * @property checkFails
52671      * Number of times we have attempted to get authentication check, and failed.
52672      * @type {Number} 
52673      */
52674     checkFails : 0,
52675       /**
52676      * @property intervalID
52677      * The window interval that does the constant login checking.
52678      * @type {Number} 
52679      */
52680     intervalID : 0,
52681     
52682     
52683     onLoad : function() // called on page load...
52684     {
52685         // load 
52686          
52687         if (Roo.get('loading')) { // clear any loading indicator..
52688             Roo.get('loading').remove();
52689         }
52690         
52691         //this.switchLang('en'); // set the language to english..
52692        
52693         this.check({
52694             success:  function(response, opts)  {  // check successfull...
52695             
52696                 var res = this.processResponse(response);
52697                 this.checkFails =0;
52698                 if (!res.success) { // error!
52699                     this.checkFails = 5;
52700                     //console.log('call failure');
52701                     return this.failure(response,opts);
52702                 }
52703                 
52704                 if (!res.data.id) { // id=0 == login failure.
52705                     return this.show();
52706                 }
52707                 
52708                               
52709                         //console.log(success);
52710                 this.fillAuth(res.data);   
52711                 this.checkFails =0;
52712                 Roo.XComponent.build();
52713             },
52714             failure : this.show
52715         });
52716         
52717     }, 
52718     
52719     
52720     check: function(cfg) // called every so often to refresh cookie etc..
52721     {
52722         if (cfg.again) { // could be undefined..
52723             this.checkFails++;
52724         } else {
52725             this.checkFails = 0;
52726         }
52727         var _this = this;
52728         if (this.sending) {
52729             if ( this.checkFails > 4) {
52730                 Roo.MessageBox.alert("Error",  
52731                     "Error getting authentication status. - try reloading, or wait a while", function() {
52732                         _this.sending = false;
52733                     }); 
52734                 return;
52735             }
52736             cfg.again = true;
52737             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52738             return;
52739         }
52740         this.sending = true;
52741         
52742         Roo.Ajax.request({  
52743             url: this.url,
52744             params: {
52745                 getAuthUser: true
52746             },  
52747             method: this.method,
52748             success:  cfg.success || this.success,
52749             failure : cfg.failure || this.failure,
52750             scope : this,
52751             callCfg : cfg
52752               
52753         });  
52754     }, 
52755     
52756     
52757     logout: function()
52758     {
52759         window.onbeforeunload = function() { }; // false does not work for IE..
52760         this.user = false;
52761         var _this = this;
52762         
52763         Roo.Ajax.request({  
52764             url: this.url,
52765             params: {
52766                 logout: 1
52767             },  
52768             method: 'GET',
52769             failure : function() {
52770                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52771                     document.location = document.location.toString() + '?ts=' + Math.random();
52772                 });
52773                 
52774             },
52775             success : function() {
52776                 _this.user = false;
52777                 this.checkFails =0;
52778                 // fixme..
52779                 document.location = document.location.toString() + '?ts=' + Math.random();
52780             }
52781               
52782               
52783         }); 
52784     },
52785     
52786     processResponse : function (response)
52787     {
52788         var res = '';
52789         try {
52790             res = Roo.decode(response.responseText);
52791             // oops...
52792             if (typeof(res) != 'object') {
52793                 res = { success : false, errorMsg : res, errors : true };
52794             }
52795             if (typeof(res.success) == 'undefined') {
52796                 res.success = false;
52797             }
52798             
52799         } catch(e) {
52800             res = { success : false,  errorMsg : response.responseText, errors : true };
52801         }
52802         return res;
52803     },
52804     
52805     success : function(response, opts)  // check successfull...
52806     {  
52807         this.sending = false;
52808         var res = this.processResponse(response);
52809         if (!res.success) {
52810             return this.failure(response, opts);
52811         }
52812         if (!res.data || !res.data.id) {
52813             return this.failure(response,opts);
52814         }
52815         //console.log(res);
52816         this.fillAuth(res.data);
52817         
52818         this.checkFails =0;
52819         
52820     },
52821     
52822     
52823     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52824     {
52825         this.authUser = -1;
52826         this.sending = false;
52827         var res = this.processResponse(response);
52828         //console.log(res);
52829         if ( this.checkFails > 2) {
52830         
52831             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52832                 "Error getting authentication status. - try reloading"); 
52833             return;
52834         }
52835         opts.callCfg.again = true;
52836         this.check.defer(1000, this, [ opts.callCfg ]);
52837         return;  
52838     },
52839     
52840     
52841     
52842     fillAuth: function(au) {
52843         this.startAuthCheck();
52844         this.authUserId = au.id;
52845         this.authUser = au;
52846         this.lastChecked = new Date();
52847         this.fireEvent('refreshed', au);
52848         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52849         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52850         au.lang = au.lang || 'en';
52851         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52852         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52853         this.switchLang(au.lang );
52854         
52855      
52856         // open system... - -on setyp..
52857         if (this.authUserId  < 0) {
52858             Roo.MessageBox.alert("Warning", 
52859                 "This is an open system - please set up a admin user with a password.");  
52860         }
52861          
52862         //Pman.onload(); // which should do nothing if it's a re-auth result...
52863         
52864              
52865     },
52866     
52867     startAuthCheck : function() // starter for timeout checking..
52868     {
52869         if (this.intervalID) { // timer already in place...
52870             return false;
52871         }
52872         var _this = this;
52873         this.intervalID =  window.setInterval(function() {
52874               _this.check(false);
52875             }, 120000); // every 120 secs = 2mins..
52876         
52877         
52878     },
52879          
52880     
52881     switchLang : function (lang) 
52882     {
52883         _T = typeof(_T) == 'undefined' ? false : _T;
52884           if (!_T || !lang.length) {
52885             return;
52886         }
52887         
52888         if (!_T && lang != 'en') {
52889             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52890             return;
52891         }
52892         
52893         if (typeof(_T.en) == 'undefined') {
52894             _T.en = {};
52895             Roo.apply(_T.en, _T);
52896         }
52897         
52898         if (typeof(_T[lang]) == 'undefined') {
52899             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52900             return;
52901         }
52902         
52903         
52904         Roo.apply(_T, _T[lang]);
52905         // just need to set the text values for everything...
52906         var _this = this;
52907         /* this will not work ...
52908         if (this.form) { 
52909             
52910                
52911             function formLabel(name, val) {
52912                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52913             }
52914             
52915             formLabel('password', "Password"+':');
52916             formLabel('username', "Email Address"+':');
52917             formLabel('lang', "Language"+':');
52918             this.dialog.setTitle("Login");
52919             this.dialog.buttons[0].setText("Forgot Password");
52920             this.dialog.buttons[1].setText("Login");
52921         }
52922         */
52923         
52924         
52925     },
52926     
52927     
52928     title: "Login",
52929     modal: true,
52930     width:  350,
52931     //height: 230,
52932     height: 180,
52933     shadow: true,
52934     minWidth:200,
52935     minHeight:180,
52936     //proxyDrag: true,
52937     closable: false,
52938     draggable: false,
52939     collapsible: false,
52940     resizable: false,
52941     center: {  // needed??
52942         autoScroll:false,
52943         titlebar: false,
52944        // tabPosition: 'top',
52945         hideTabs: true,
52946         closeOnTab: true,
52947         alwaysShowTabs: false
52948     } ,
52949     listeners : {
52950         
52951         show  : function(dlg)
52952         {
52953             //console.log(this);
52954             this.form = this.layout.getRegion('center').activePanel.form;
52955             this.form.dialog = dlg;
52956             this.buttons[0].form = this.form;
52957             this.buttons[0].dialog = dlg;
52958             this.buttons[1].form = this.form;
52959             this.buttons[1].dialog = dlg;
52960            
52961            //this.resizeToLogo.defer(1000,this);
52962             // this is all related to resizing for logos..
52963             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52964            //// if (!sz) {
52965              //   this.resizeToLogo.defer(1000,this);
52966              //   return;
52967            // }
52968             //var w = Ext.lib.Dom.getViewWidth() - 100;
52969             //var h = Ext.lib.Dom.getViewHeight() - 100;
52970             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52971             //this.center();
52972             if (this.disabled) {
52973                 this.hide();
52974                 return;
52975             }
52976             
52977             if (this.user.id < 0) { // used for inital setup situations.
52978                 return;
52979             }
52980             
52981             if (this.intervalID) {
52982                 // remove the timer
52983                 window.clearInterval(this.intervalID);
52984                 this.intervalID = false;
52985             }
52986             
52987             
52988             if (Roo.get('loading')) {
52989                 Roo.get('loading').remove();
52990             }
52991             if (Roo.get('loading-mask')) {
52992                 Roo.get('loading-mask').hide();
52993             }
52994             
52995             //incomming._node = tnode;
52996             this.form.reset();
52997             //this.dialog.modal = !modal;
52998             //this.dialog.show();
52999             this.el.unmask(); 
53000             
53001             
53002             this.form.setValues({
53003                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53004                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53005             });
53006             
53007             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53008             if (this.form.findField('username').getValue().length > 0 ){
53009                 this.form.findField('password').focus();
53010             } else {
53011                this.form.findField('username').focus();
53012             }
53013     
53014         }
53015     },
53016     items : [
53017          {
53018        
53019             xtype : 'ContentPanel',
53020             xns : Roo,
53021             region: 'center',
53022             fitToFrame : true,
53023             
53024             items : [
53025     
53026                 {
53027                
53028                     xtype : 'Form',
53029                     xns : Roo.form,
53030                     labelWidth: 100,
53031                     style : 'margin: 10px;',
53032                     
53033                     listeners : {
53034                         actionfailed : function(f, act) {
53035                             // form can return { errors: .... }
53036                                 
53037                             //act.result.errors // invalid form element list...
53038                             //act.result.errorMsg// invalid form element list...
53039                             
53040                             this.dialog.el.unmask();
53041                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53042                                         "Login failed - communication error - try again.");
53043                                       
53044                         },
53045                         actioncomplete: function(re, act) {
53046                              
53047                             Roo.state.Manager.set(
53048                                 this.dialog.realm + '.username',  
53049                                     this.findField('username').getValue()
53050                             );
53051                             Roo.state.Manager.set(
53052                                 this.dialog.realm + '.lang',  
53053                                 this.findField('lang').getValue() 
53054                             );
53055                             
53056                             this.dialog.fillAuth(act.result.data);
53057                               
53058                             this.dialog.hide();
53059                             
53060                             if (Roo.get('loading-mask')) {
53061                                 Roo.get('loading-mask').show();
53062                             }
53063                             Roo.XComponent.build();
53064                             
53065                              
53066                             
53067                         }
53068                     },
53069                     items : [
53070                         {
53071                             xtype : 'TextField',
53072                             xns : Roo.form,
53073                             fieldLabel: "Email Address",
53074                             name: 'username',
53075                             width:200,
53076                             autoCreate : {tag: "input", type: "text", size: "20"}
53077                         },
53078                         {
53079                             xtype : 'TextField',
53080                             xns : Roo.form,
53081                             fieldLabel: "Password",
53082                             inputType: 'password',
53083                             name: 'password',
53084                             width:200,
53085                             autoCreate : {tag: "input", type: "text", size: "20"},
53086                             listeners : {
53087                                 specialkey : function(e,ev) {
53088                                     if (ev.keyCode == 13) {
53089                                         this.form.dialog.el.mask("Logging in");
53090                                         this.form.doAction('submit', {
53091                                             url: this.form.dialog.url,
53092                                             method: this.form.dialog.method
53093                                         });
53094                                     }
53095                                 }
53096                             }  
53097                         },
53098                         {
53099                             xtype : 'ComboBox',
53100                             xns : Roo.form,
53101                             fieldLabel: "Language",
53102                             name : 'langdisp',
53103                             store: {
53104                                 xtype : 'SimpleStore',
53105                                 fields: ['lang', 'ldisp'],
53106                                 data : [
53107                                     [ 'en', 'English' ],
53108                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53109                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53110                                 ]
53111                             },
53112                             
53113                             valueField : 'lang',
53114                             hiddenName:  'lang',
53115                             width: 200,
53116                             displayField:'ldisp',
53117                             typeAhead: false,
53118                             editable: false,
53119                             mode: 'local',
53120                             triggerAction: 'all',
53121                             emptyText:'Select a Language...',
53122                             selectOnFocus:true,
53123                             listeners : {
53124                                 select :  function(cb, rec, ix) {
53125                                     this.form.switchLang(rec.data.lang);
53126                                 }
53127                             }
53128                         
53129                         }
53130                     ]
53131                 }
53132                   
53133                 
53134             ]
53135         }
53136     ],
53137     buttons : [
53138         {
53139             xtype : 'Button',
53140             xns : 'Roo',
53141             text : "Forgot Password",
53142             listeners : {
53143                 click : function() {
53144                     //console.log(this);
53145                     var n = this.form.findField('username').getValue();
53146                     if (!n.length) {
53147                         Roo.MessageBox.alert("Error", "Fill in your email address");
53148                         return;
53149                     }
53150                     Roo.Ajax.request({
53151                         url: this.dialog.url,
53152                         params: {
53153                             passwordRequest: n
53154                         },
53155                         method: this.dialog.method,
53156                         success:  function(response, opts)  {  // check successfull...
53157                         
53158                             var res = this.dialog.processResponse(response);
53159                             if (!res.success) { // error!
53160                                Roo.MessageBox.alert("Error" ,
53161                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53162                                return;
53163                             }
53164                             Roo.MessageBox.alert("Notice" ,
53165                                 "Please check you email for the Password Reset message");
53166                         },
53167                         failure : function() {
53168                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53169                         }
53170                         
53171                     });
53172                 }
53173             }
53174         },
53175         {
53176             xtype : 'Button',
53177             xns : 'Roo',
53178             text : "Login",
53179             listeners : {
53180                 
53181                 click : function () {
53182                         
53183                     this.dialog.el.mask("Logging in");
53184                     this.form.doAction('submit', {
53185                             url: this.dialog.url,
53186                             method: this.dialog.method
53187                     });
53188                 }
53189             }
53190         }
53191     ]
53192   
53193   
53194 })
53195  
53196
53197
53198