roojs-core.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 (Allows +08, without minutes)
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{2,4})"};
1391     
1392     
1393     case "P":
1394         return {g:1,
1395                 c:[
1396                    "o = results[", currentGroup, "];\n",
1397                    "var sn = o.substring(0,1);\n",
1398                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1399                    "var mn = o.substring(4,6) % 60;\n",
1400                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1401                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1402             ].join(""),
1403             s:"([+\-]\\d{4})"};
1404     case "T":
1405         return {g:0,
1406             c:null,
1407             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1408     case "Z":
1409         return {g:1,
1410             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1411                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1412             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1413     default:
1414         return {g:0,
1415             c:null,
1416             s:String.escape(character)};
1417     }
1418 };
1419
1420 /**
1421  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1422  * @return {String} The abbreviated timezone name (e.g. 'CST')
1423  */
1424 Date.prototype.getTimezone = function() {
1425     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1426 };
1427
1428 /**
1429  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1430  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1431  */
1432 Date.prototype.getGMTOffset = function() {
1433     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1434         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1435         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1436 };
1437
1438 /**
1439  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1440  * @return {String} 2-characters representing hours and 2-characters representing minutes
1441  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1442  */
1443 Date.prototype.getGMTColonOffset = function() {
1444         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1445                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1446                 + ":"
1447                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1448 }
1449
1450 /**
1451  * Get the numeric day number of the year, adjusted for leap year.
1452  * @return {Number} 0 through 364 (365 in leap years)
1453  */
1454 Date.prototype.getDayOfYear = function() {
1455     var num = 0;
1456     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1457     for (var i = 0; i < this.getMonth(); ++i) {
1458         num += Date.daysInMonth[i];
1459     }
1460     return num + this.getDate() - 1;
1461 };
1462
1463 /**
1464  * Get the string representation of the numeric week number of the year
1465  * (equivalent to the format specifier 'W').
1466  * @return {String} '00' through '52'
1467  */
1468 Date.prototype.getWeekOfYear = function() {
1469     // Skip to Thursday of this week
1470     var now = this.getDayOfYear() + (4 - this.getDay());
1471     // Find the first Thursday of the year
1472     var jan1 = new Date(this.getFullYear(), 0, 1);
1473     var then = (7 - jan1.getDay() + 4);
1474     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1475 };
1476
1477 /**
1478  * Whether or not the current date is in a leap year.
1479  * @return {Boolean} True if the current date is in a leap year, else false
1480  */
1481 Date.prototype.isLeapYear = function() {
1482     var year = this.getFullYear();
1483     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1484 };
1485
1486 /**
1487  * Get the first day of the current month, adjusted for leap year.  The returned value
1488  * is the numeric day index within the week (0-6) which can be used in conjunction with
1489  * the {@link #monthNames} array to retrieve the textual day name.
1490  * Example:
1491  *<pre><code>
1492 var dt = new Date('1/10/2007');
1493 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1494 </code></pre>
1495  * @return {Number} The day number (0-6)
1496  */
1497 Date.prototype.getFirstDayOfMonth = function() {
1498     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1499     return (day < 0) ? (day + 7) : day;
1500 };
1501
1502 /**
1503  * Get the last day of the current month, adjusted for leap year.  The returned value
1504  * is the numeric day index within the week (0-6) which can be used in conjunction with
1505  * the {@link #monthNames} array to retrieve the textual day name.
1506  * Example:
1507  *<pre><code>
1508 var dt = new Date('1/10/2007');
1509 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1510 </code></pre>
1511  * @return {Number} The day number (0-6)
1512  */
1513 Date.prototype.getLastDayOfMonth = function() {
1514     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1515     return (day < 0) ? (day + 7) : day;
1516 };
1517
1518
1519 /**
1520  * Get the first date of this date's month
1521  * @return {Date}
1522  */
1523 Date.prototype.getFirstDateOfMonth = function() {
1524     return new Date(this.getFullYear(), this.getMonth(), 1);
1525 };
1526
1527 /**
1528  * Get the last date of this date's month
1529  * @return {Date}
1530  */
1531 Date.prototype.getLastDateOfMonth = function() {
1532     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1533 };
1534 /**
1535  * Get the number of days in the current month, adjusted for leap year.
1536  * @return {Number} The number of days in the month
1537  */
1538 Date.prototype.getDaysInMonth = function() {
1539     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1540     return Date.daysInMonth[this.getMonth()];
1541 };
1542
1543 /**
1544  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1545  * @return {String} 'st, 'nd', 'rd' or 'th'
1546  */
1547 Date.prototype.getSuffix = function() {
1548     switch (this.getDate()) {
1549         case 1:
1550         case 21:
1551         case 31:
1552             return "st";
1553         case 2:
1554         case 22:
1555             return "nd";
1556         case 3:
1557         case 23:
1558             return "rd";
1559         default:
1560             return "th";
1561     }
1562 };
1563
1564 // private
1565 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1566
1567 /**
1568  * An array of textual month names.
1569  * Override these values for international dates, for example...
1570  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1571  * @type Array
1572  * @static
1573  */
1574 Date.monthNames =
1575    ["January",
1576     "February",
1577     "March",
1578     "April",
1579     "May",
1580     "June",
1581     "July",
1582     "August",
1583     "September",
1584     "October",
1585     "November",
1586     "December"];
1587
1588 /**
1589  * An array of textual day names.
1590  * Override these values for international dates, for example...
1591  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1592  * @type Array
1593  * @static
1594  */
1595 Date.dayNames =
1596    ["Sunday",
1597     "Monday",
1598     "Tuesday",
1599     "Wednesday",
1600     "Thursday",
1601     "Friday",
1602     "Saturday"];
1603
1604 // private
1605 Date.y2kYear = 50;
1606 // private
1607 Date.monthNumbers = {
1608     Jan:0,
1609     Feb:1,
1610     Mar:2,
1611     Apr:3,
1612     May:4,
1613     Jun:5,
1614     Jul:6,
1615     Aug:7,
1616     Sep:8,
1617     Oct:9,
1618     Nov:10,
1619     Dec:11};
1620
1621 /**
1622  * Creates and returns a new Date instance with the exact same date value as the called instance.
1623  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1624  * variable will also be changed.  When the intention is to create a new variable that will not
1625  * modify the original instance, you should create a clone.
1626  *
1627  * Example of correctly cloning a date:
1628  * <pre><code>
1629 //wrong way:
1630 var orig = new Date('10/1/2006');
1631 var copy = orig;
1632 copy.setDate(5);
1633 document.write(orig);  //returns 'Thu Oct 05 2006'!
1634
1635 //correct way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig.clone();
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 01 2006'
1640 </code></pre>
1641  * @return {Date} The new Date instance
1642  */
1643 Date.prototype.clone = function() {
1644         return new Date(this.getTime());
1645 };
1646
1647 /**
1648  * Clears any time information from this date
1649  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1650  @return {Date} this or the clone
1651  */
1652 Date.prototype.clearTime = function(clone){
1653     if(clone){
1654         return this.clone().clearTime();
1655     }
1656     this.setHours(0);
1657     this.setMinutes(0);
1658     this.setSeconds(0);
1659     this.setMilliseconds(0);
1660     return this;
1661 };
1662
1663 // private
1664 // safari setMonth is broken
1665 if(Roo.isSafari){
1666     Date.brokenSetMonth = Date.prototype.setMonth;
1667         Date.prototype.setMonth = function(num){
1668                 if(num <= -1){
1669                         var n = Math.ceil(-num);
1670                         var back_year = Math.ceil(n/12);
1671                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1672                         this.setFullYear(this.getFullYear() - back_year);
1673                         return Date.brokenSetMonth.call(this, month);
1674                 } else {
1675                         return Date.brokenSetMonth.apply(this, arguments);
1676                 }
1677         };
1678 }
1679
1680 /** Date interval constant 
1681 * @static 
1682 * @type String */
1683 Date.MILLI = "ms";
1684 /** Date interval constant 
1685 * @static 
1686 * @type String */
1687 Date.SECOND = "s";
1688 /** Date interval constant 
1689 * @static 
1690 * @type String */
1691 Date.MINUTE = "mi";
1692 /** Date interval constant 
1693 * @static 
1694 * @type String */
1695 Date.HOUR = "h";
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.DAY = "d";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.MONTH = "mo";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.YEAR = "y";
1708
1709 /**
1710  * Provides a convenient method of performing basic date arithmetic.  This method
1711  * does not modify the Date instance being called - it creates and returns
1712  * a new Date instance containing the resulting date value.
1713  *
1714  * Examples:
1715  * <pre><code>
1716 //Basic usage:
1717 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1718 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1719
1720 //Negative values will subtract correctly:
1721 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1722 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1723
1724 //You can even chain several calls together in one line!
1725 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1726 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1727  </code></pre>
1728  *
1729  * @param {String} interval   A valid date interval enum value
1730  * @param {Number} value      The amount to add to the current date
1731  * @return {Date} The new Date instance
1732  */
1733 Date.prototype.add = function(interval, value){
1734   var d = this.clone();
1735   if (!interval || value === 0) return d;
1736   switch(interval.toLowerCase()){
1737     case Date.MILLI:
1738       d.setMilliseconds(this.getMilliseconds() + value);
1739       break;
1740     case Date.SECOND:
1741       d.setSeconds(this.getSeconds() + value);
1742       break;
1743     case Date.MINUTE:
1744       d.setMinutes(this.getMinutes() + value);
1745       break;
1746     case Date.HOUR:
1747       d.setHours(this.getHours() + value);
1748       break;
1749     case Date.DAY:
1750       d.setDate(this.getDate() + value);
1751       break;
1752     case Date.MONTH:
1753       var day = this.getDate();
1754       if(day > 28){
1755           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1756       }
1757       d.setDate(day);
1758       d.setMonth(this.getMonth() + value);
1759       break;
1760     case Date.YEAR:
1761       d.setFullYear(this.getFullYear() + value);
1762       break;
1763   }
1764   return d;
1765 };
1766 /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776
1777 Roo.lib.Dom = {
1778     getViewWidth : function(full) {
1779         return full ? this.getDocumentWidth() : this.getViewportWidth();
1780     },
1781
1782     getViewHeight : function(full) {
1783         return full ? this.getDocumentHeight() : this.getViewportHeight();
1784     },
1785
1786     getDocumentHeight: function() {
1787         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1788         return Math.max(scrollHeight, this.getViewportHeight());
1789     },
1790
1791     getDocumentWidth: function() {
1792         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1793         return Math.max(scrollWidth, this.getViewportWidth());
1794     },
1795
1796     getViewportHeight: function() {
1797         var height = self.innerHeight;
1798         var mode = document.compatMode;
1799
1800         if ((mode || Roo.isIE) && !Roo.isOpera) {
1801             height = (mode == "CSS1Compat") ?
1802                      document.documentElement.clientHeight :
1803                      document.body.clientHeight;
1804         }
1805
1806         return height;
1807     },
1808
1809     getViewportWidth: function() {
1810         var width = self.innerWidth;
1811         var mode = document.compatMode;
1812
1813         if (mode || Roo.isIE) {
1814             width = (mode == "CSS1Compat") ?
1815                     document.documentElement.clientWidth :
1816                     document.body.clientWidth;
1817         }
1818         return width;
1819     },
1820
1821     isAncestor : function(p, c) {
1822         p = Roo.getDom(p);
1823         c = Roo.getDom(c);
1824         if (!p || !c) {
1825             return false;
1826         }
1827
1828         if (p.contains && !Roo.isSafari) {
1829             return p.contains(c);
1830         } else if (p.compareDocumentPosition) {
1831             return !!(p.compareDocumentPosition(c) & 16);
1832         } else {
1833             var parent = c.parentNode;
1834             while (parent) {
1835                 if (parent == p) {
1836                     return true;
1837                 }
1838                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1839                     return false;
1840                 }
1841                 parent = parent.parentNode;
1842             }
1843             return false;
1844         }
1845     },
1846
1847     getRegion : function(el) {
1848         return Roo.lib.Region.getRegion(el);
1849     },
1850
1851     getY : function(el) {
1852         return this.getXY(el)[1];
1853     },
1854
1855     getX : function(el) {
1856         return this.getXY(el)[0];
1857     },
1858
1859     getXY : function(el) {
1860         var p, pe, b, scroll, bd = document.body;
1861         el = Roo.getDom(el);
1862         var fly = Roo.lib.AnimBase.fly;
1863         if (el.getBoundingClientRect) {
1864             b = el.getBoundingClientRect();
1865             scroll = fly(document).getScroll();
1866             return [b.left + scroll.left, b.top + scroll.top];
1867         }
1868         var x = 0, y = 0;
1869
1870         p = el;
1871
1872         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1873
1874         while (p) {
1875
1876             x += p.offsetLeft;
1877             y += p.offsetTop;
1878
1879             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1880                 hasAbsolute = true;
1881             }
1882
1883             if (Roo.isGecko) {
1884                 pe = fly(p);
1885
1886                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1887                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1888
1889
1890                 x += bl;
1891                 y += bt;
1892
1893
1894                 if (p != el && pe.getStyle('overflow') != 'visible') {
1895                     x += bl;
1896                     y += bt;
1897                 }
1898             }
1899             p = p.offsetParent;
1900         }
1901
1902         if (Roo.isSafari && hasAbsolute) {
1903             x -= bd.offsetLeft;
1904             y -= bd.offsetTop;
1905         }
1906
1907         if (Roo.isGecko && !hasAbsolute) {
1908             var dbd = fly(bd);
1909             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1910             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1911         }
1912
1913         p = el.parentNode;
1914         while (p && p != bd) {
1915             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1916                 x -= p.scrollLeft;
1917                 y -= p.scrollTop;
1918             }
1919             p = p.parentNode;
1920         }
1921         return [x, y];
1922     },
1923  
1924   
1925
1926
1927     setXY : function(el, xy) {
1928         el = Roo.fly(el, '_setXY');
1929         el.position();
1930         var pts = el.translatePoints(xy);
1931         if (xy[0] !== false) {
1932             el.dom.style.left = pts.left + "px";
1933         }
1934         if (xy[1] !== false) {
1935             el.dom.style.top = pts.top + "px";
1936         }
1937     },
1938
1939     setX : function(el, x) {
1940         this.setXY(el, [x, false]);
1941     },
1942
1943     setY : function(el, y) {
1944         this.setXY(el, [false, y]);
1945     }
1946 };
1947 /*
1948  * Portions of this file are based on pieces of Yahoo User Interface Library
1949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1950  * YUI licensed under the BSD License:
1951  * http://developer.yahoo.net/yui/license.txt
1952  * <script type="text/javascript">
1953  *
1954  */
1955
1956 Roo.lib.Event = function() {
1957     var loadComplete = false;
1958     var listeners = [];
1959     var unloadListeners = [];
1960     var retryCount = 0;
1961     var onAvailStack = [];
1962     var counter = 0;
1963     var lastError = null;
1964
1965     return {
1966         POLL_RETRYS: 200,
1967         POLL_INTERVAL: 20,
1968         EL: 0,
1969         TYPE: 1,
1970         FN: 2,
1971         WFN: 3,
1972         OBJ: 3,
1973         ADJ_SCOPE: 4,
1974         _interval: null,
1975
1976         startInterval: function() {
1977             if (!this._interval) {
1978                 var self = this;
1979                 var callback = function() {
1980                     self._tryPreloadAttach();
1981                 };
1982                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1983
1984             }
1985         },
1986
1987         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1988             onAvailStack.push({ id:         p_id,
1989                 fn:         p_fn,
1990                 obj:        p_obj,
1991                 override:   p_override,
1992                 checkReady: false    });
1993
1994             retryCount = this.POLL_RETRYS;
1995             this.startInterval();
1996         },
1997
1998
1999         addListener: function(el, eventName, fn) {
2000             el = Roo.getDom(el);
2001             if (!el || !fn) {
2002                 return false;
2003             }
2004
2005             if ("unload" == eventName) {
2006                 unloadListeners[unloadListeners.length] =
2007                 [el, eventName, fn];
2008                 return true;
2009             }
2010
2011             var wrappedFn = function(e) {
2012                 return fn(Roo.lib.Event.getEvent(e));
2013             };
2014
2015             var li = [el, eventName, fn, wrappedFn];
2016
2017             var index = listeners.length;
2018             listeners[index] = li;
2019
2020             this.doAdd(el, eventName, wrappedFn, false);
2021             return true;
2022
2023         },
2024
2025
2026         removeListener: function(el, eventName, fn) {
2027             var i, len;
2028
2029             el = Roo.getDom(el);
2030
2031             if(!fn) {
2032                 return this.purgeElement(el, false, eventName);
2033             }
2034
2035
2036             if ("unload" == eventName) {
2037
2038                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2039                     var li = unloadListeners[i];
2040                     if (li &&
2041                         li[0] == el &&
2042                         li[1] == eventName &&
2043                         li[2] == fn) {
2044                         unloadListeners.splice(i, 1);
2045                         return true;
2046                     }
2047                 }
2048
2049                 return false;
2050             }
2051
2052             var cacheItem = null;
2053
2054
2055             var index = arguments[3];
2056
2057             if ("undefined" == typeof index) {
2058                 index = this._getCacheIndex(el, eventName, fn);
2059             }
2060
2061             if (index >= 0) {
2062                 cacheItem = listeners[index];
2063             }
2064
2065             if (!el || !cacheItem) {
2066                 return false;
2067             }
2068
2069             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2070
2071             delete listeners[index][this.WFN];
2072             delete listeners[index][this.FN];
2073             listeners.splice(index, 1);
2074
2075             return true;
2076
2077         },
2078
2079
2080         getTarget: function(ev, resolveTextNode) {
2081             ev = ev.browserEvent || ev;
2082             var t = ev.target || ev.srcElement;
2083             return this.resolveTextNode(t);
2084         },
2085
2086
2087         resolveTextNode: function(node) {
2088             if (Roo.isSafari && node && 3 == node.nodeType) {
2089                 return node.parentNode;
2090             } else {
2091                 return node;
2092             }
2093         },
2094
2095
2096         getPageX: function(ev) {
2097             ev = ev.browserEvent || ev;
2098             var x = ev.pageX;
2099             if (!x && 0 !== x) {
2100                 x = ev.clientX || 0;
2101
2102                 if (Roo.isIE) {
2103                     x += this.getScroll()[1];
2104                 }
2105             }
2106
2107             return x;
2108         },
2109
2110
2111         getPageY: function(ev) {
2112             ev = ev.browserEvent || ev;
2113             var y = ev.pageY;
2114             if (!y && 0 !== y) {
2115                 y = ev.clientY || 0;
2116
2117                 if (Roo.isIE) {
2118                     y += this.getScroll()[0];
2119                 }
2120             }
2121
2122
2123             return y;
2124         },
2125
2126
2127         getXY: function(ev) {
2128             ev = ev.browserEvent || ev;
2129             return [this.getPageX(ev), this.getPageY(ev)];
2130         },
2131
2132
2133         getRelatedTarget: function(ev) {
2134             ev = ev.browserEvent || ev;
2135             var t = ev.relatedTarget;
2136             if (!t) {
2137                 if (ev.type == "mouseout") {
2138                     t = ev.toElement;
2139                 } else if (ev.type == "mouseover") {
2140                     t = ev.fromElement;
2141                 }
2142             }
2143
2144             return this.resolveTextNode(t);
2145         },
2146
2147
2148         getTime: function(ev) {
2149             ev = ev.browserEvent || ev;
2150             if (!ev.time) {
2151                 var t = new Date().getTime();
2152                 try {
2153                     ev.time = t;
2154                 } catch(ex) {
2155                     this.lastError = ex;
2156                     return t;
2157                 }
2158             }
2159
2160             return ev.time;
2161         },
2162
2163
2164         stopEvent: function(ev) {
2165             this.stopPropagation(ev);
2166             this.preventDefault(ev);
2167         },
2168
2169
2170         stopPropagation: function(ev) {
2171             ev = ev.browserEvent || ev;
2172             if (ev.stopPropagation) {
2173                 ev.stopPropagation();
2174             } else {
2175                 ev.cancelBubble = true;
2176             }
2177         },
2178
2179
2180         preventDefault: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             if(ev.preventDefault) {
2183                 ev.preventDefault();
2184             } else {
2185                 ev.returnValue = false;
2186             }
2187         },
2188
2189
2190         getEvent: function(e) {
2191             var ev = e || window.event;
2192             if (!ev) {
2193                 var c = this.getEvent.caller;
2194                 while (c) {
2195                     ev = c.arguments[0];
2196                     if (ev && Event == ev.constructor) {
2197                         break;
2198                     }
2199                     c = c.caller;
2200                 }
2201             }
2202             return ev;
2203         },
2204
2205
2206         getCharCode: function(ev) {
2207             ev = ev.browserEvent || ev;
2208             return ev.charCode || ev.keyCode || 0;
2209         },
2210
2211
2212         _getCacheIndex: function(el, eventName, fn) {
2213             for (var i = 0,len = listeners.length; i < len; ++i) {
2214                 var li = listeners[i];
2215                 if (li &&
2216                     li[this.FN] == fn &&
2217                     li[this.EL] == el &&
2218                     li[this.TYPE] == eventName) {
2219                     return i;
2220                 }
2221             }
2222
2223             return -1;
2224         },
2225
2226
2227         elCache: {},
2228
2229
2230         getEl: function(id) {
2231             return document.getElementById(id);
2232         },
2233
2234
2235         clearCache: function() {
2236         },
2237
2238
2239         _load: function(e) {
2240             loadComplete = true;
2241             var EU = Roo.lib.Event;
2242
2243
2244             if (Roo.isIE) {
2245                 EU.doRemove(window, "load", EU._load);
2246             }
2247         },
2248
2249
2250         _tryPreloadAttach: function() {
2251
2252             if (this.locked) {
2253                 return false;
2254             }
2255
2256             this.locked = true;
2257
2258
2259             var tryAgain = !loadComplete;
2260             if (!tryAgain) {
2261                 tryAgain = (retryCount > 0);
2262             }
2263
2264
2265             var notAvail = [];
2266             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2267                 var item = onAvailStack[i];
2268                 if (item) {
2269                     var el = this.getEl(item.id);
2270
2271                     if (el) {
2272                         if (!item.checkReady ||
2273                             loadComplete ||
2274                             el.nextSibling ||
2275                             (document && document.body)) {
2276
2277                             var scope = el;
2278                             if (item.override) {
2279                                 if (item.override === true) {
2280                                     scope = item.obj;
2281                                 } else {
2282                                     scope = item.override;
2283                                 }
2284                             }
2285                             item.fn.call(scope, item.obj);
2286                             onAvailStack[i] = null;
2287                         }
2288                     } else {
2289                         notAvail.push(item);
2290                     }
2291                 }
2292             }
2293
2294             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2295
2296             if (tryAgain) {
2297
2298                 this.startInterval();
2299             } else {
2300                 clearInterval(this._interval);
2301                 this._interval = null;
2302             }
2303
2304             this.locked = false;
2305
2306             return true;
2307
2308         },
2309
2310
2311         purgeElement: function(el, recurse, eventName) {
2312             var elListeners = this.getListeners(el, eventName);
2313             if (elListeners) {
2314                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2315                     var l = elListeners[i];
2316                     this.removeListener(el, l.type, l.fn);
2317                 }
2318             }
2319
2320             if (recurse && el && el.childNodes) {
2321                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2322                     this.purgeElement(el.childNodes[i], recurse, eventName);
2323                 }
2324             }
2325         },
2326
2327
2328         getListeners: function(el, eventName) {
2329             var results = [], searchLists;
2330             if (!eventName) {
2331                 searchLists = [listeners, unloadListeners];
2332             } else if (eventName == "unload") {
2333                 searchLists = [unloadListeners];
2334             } else {
2335                 searchLists = [listeners];
2336             }
2337
2338             for (var j = 0; j < searchLists.length; ++j) {
2339                 var searchList = searchLists[j];
2340                 if (searchList && searchList.length > 0) {
2341                     for (var i = 0,len = searchList.length; i < len; ++i) {
2342                         var l = searchList[i];
2343                         if (l && l[this.EL] === el &&
2344                             (!eventName || eventName === l[this.TYPE])) {
2345                             results.push({
2346                                 type:   l[this.TYPE],
2347                                 fn:     l[this.FN],
2348                                 obj:    l[this.OBJ],
2349                                 adjust: l[this.ADJ_SCOPE],
2350                                 index:  i
2351                             });
2352                         }
2353                     }
2354                 }
2355             }
2356
2357             return (results.length) ? results : null;
2358         },
2359
2360
2361         _unload: function(e) {
2362
2363             var EU = Roo.lib.Event, i, j, l, len, index;
2364
2365             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2366                 l = unloadListeners[i];
2367                 if (l) {
2368                     var scope = window;
2369                     if (l[EU.ADJ_SCOPE]) {
2370                         if (l[EU.ADJ_SCOPE] === true) {
2371                             scope = l[EU.OBJ];
2372                         } else {
2373                             scope = l[EU.ADJ_SCOPE];
2374                         }
2375                     }
2376                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2377                     unloadListeners[i] = null;
2378                     l = null;
2379                     scope = null;
2380                 }
2381             }
2382
2383             unloadListeners = null;
2384
2385             if (listeners && listeners.length > 0) {
2386                 j = listeners.length;
2387                 while (j) {
2388                     index = j - 1;
2389                     l = listeners[index];
2390                     if (l) {
2391                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2392                                 l[EU.FN], index);
2393                     }
2394                     j = j - 1;
2395                 }
2396                 l = null;
2397
2398                 EU.clearCache();
2399             }
2400
2401             EU.doRemove(window, "unload", EU._unload);
2402
2403         },
2404
2405
2406         getScroll: function() {
2407             var dd = document.documentElement, db = document.body;
2408             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2409                 return [dd.scrollTop, dd.scrollLeft];
2410             } else if (db) {
2411                 return [db.scrollTop, db.scrollLeft];
2412             } else {
2413                 return [0, 0];
2414             }
2415         },
2416
2417
2418         doAdd: function () {
2419             if (window.addEventListener) {
2420                 return function(el, eventName, fn, capture) {
2421                     el.addEventListener(eventName, fn, (capture));
2422                 };
2423             } else if (window.attachEvent) {
2424                 return function(el, eventName, fn, capture) {
2425                     el.attachEvent("on" + eventName, fn);
2426                 };
2427             } else {
2428                 return function() {
2429                 };
2430             }
2431         }(),
2432
2433
2434         doRemove: function() {
2435             if (window.removeEventListener) {
2436                 return function (el, eventName, fn, capture) {
2437                     el.removeEventListener(eventName, fn, (capture));
2438                 };
2439             } else if (window.detachEvent) {
2440                 return function (el, eventName, fn) {
2441                     el.detachEvent("on" + eventName, fn);
2442                 };
2443             } else {
2444                 return function() {
2445                 };
2446             }
2447         }()
2448     };
2449     
2450 }();
2451 (function() {     
2452    
2453     var E = Roo.lib.Event;
2454     E.on = E.addListener;
2455     E.un = E.removeListener;
2456
2457     if (document && document.body) {
2458         E._load();
2459     } else {
2460         E.doAdd(window, "load", E._load);
2461     }
2462     E.doAdd(window, "unload", E._unload);
2463     E._tryPreloadAttach();
2464 })();
2465
2466 /*
2467  * Portions of this file are based on pieces of Yahoo User Interface Library
2468  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2469  * YUI licensed under the BSD License:
2470  * http://developer.yahoo.net/yui/license.txt
2471  * <script type="text/javascript">
2472  *
2473  */
2474
2475 (function() {
2476     /**
2477      * @class Roo.lib.Ajax
2478      *
2479      */
2480     Roo.lib.Ajax = {
2481         /**
2482          * @static 
2483          */
2484         request : function(method, uri, cb, data, options) {
2485             if(options){
2486                 var hs = options.headers;
2487                 if(hs){
2488                     for(var h in hs){
2489                         if(hs.hasOwnProperty(h)){
2490                             this.initHeader(h, hs[h], false);
2491                         }
2492                     }
2493                 }
2494                 if(options.xmlData){
2495                     this.initHeader('Content-Type', 'text/xml', false);
2496                     method = 'POST';
2497                     data = options.xmlData;
2498                 }
2499             }
2500
2501             return this.asyncRequest(method, uri, cb, data);
2502         },
2503
2504         serializeForm : function(form) {
2505             if(typeof form == 'string') {
2506                 form = (document.getElementById(form) || document.forms[form]);
2507             }
2508
2509             var el, name, val, disabled, data = '', hasSubmit = false;
2510             for (var i = 0; i < form.elements.length; i++) {
2511                 el = form.elements[i];
2512                 disabled = form.elements[i].disabled;
2513                 name = form.elements[i].name;
2514                 val = form.elements[i].value;
2515
2516                 if (!disabled && name){
2517                     switch (el.type)
2518                             {
2519                         case 'select-one':
2520                         case 'select-multiple':
2521                             for (var j = 0; j < el.options.length; j++) {
2522                                 if (el.options[j].selected) {
2523                                     if (Roo.isIE) {
2524                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2525                                     }
2526                                     else {
2527                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2528                                     }
2529                                 }
2530                             }
2531                             break;
2532                         case 'radio':
2533                         case 'checkbox':
2534                             if (el.checked) {
2535                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2536                             }
2537                             break;
2538                         case 'file':
2539
2540                         case undefined:
2541
2542                         case 'reset':
2543
2544                         case 'button':
2545
2546                             break;
2547                         case 'submit':
2548                             if(hasSubmit == false) {
2549                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2550                                 hasSubmit = true;
2551                             }
2552                             break;
2553                         default:
2554                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2555                             break;
2556                     }
2557                 }
2558             }
2559             data = data.substr(0, data.length - 1);
2560             return data;
2561         },
2562
2563         headers:{},
2564
2565         hasHeaders:false,
2566
2567         useDefaultHeader:true,
2568
2569         defaultPostHeader:'application/x-www-form-urlencoded',
2570
2571         useDefaultXhrHeader:true,
2572
2573         defaultXhrHeader:'XMLHttpRequest',
2574
2575         hasDefaultHeaders:true,
2576
2577         defaultHeaders:{},
2578
2579         poll:{},
2580
2581         timeout:{},
2582
2583         pollInterval:50,
2584
2585         transactionId:0,
2586
2587         setProgId:function(id)
2588         {
2589             this.activeX.unshift(id);
2590         },
2591
2592         setDefaultPostHeader:function(b)
2593         {
2594             this.useDefaultHeader = b;
2595         },
2596
2597         setDefaultXhrHeader:function(b)
2598         {
2599             this.useDefaultXhrHeader = b;
2600         },
2601
2602         setPollingInterval:function(i)
2603         {
2604             if (typeof i == 'number' && isFinite(i)) {
2605                 this.pollInterval = i;
2606             }
2607         },
2608
2609         createXhrObject:function(transactionId)
2610         {
2611             var obj,http;
2612             try
2613             {
2614
2615                 http = new XMLHttpRequest();
2616
2617                 obj = { conn:http, tId:transactionId };
2618             }
2619             catch(e)
2620             {
2621                 for (var i = 0; i < this.activeX.length; ++i) {
2622                     try
2623                     {
2624
2625                         http = new ActiveXObject(this.activeX[i]);
2626
2627                         obj = { conn:http, tId:transactionId };
2628                         break;
2629                     }
2630                     catch(e) {
2631                     }
2632                 }
2633             }
2634             finally
2635             {
2636                 return obj;
2637             }
2638         },
2639
2640         getConnectionObject:function()
2641         {
2642             var o;
2643             var tId = this.transactionId;
2644
2645             try
2646             {
2647                 o = this.createXhrObject(tId);
2648                 if (o) {
2649                     this.transactionId++;
2650                 }
2651             }
2652             catch(e) {
2653             }
2654             finally
2655             {
2656                 return o;
2657             }
2658         },
2659
2660         asyncRequest:function(method, uri, callback, postData)
2661         {
2662             var o = this.getConnectionObject();
2663
2664             if (!o) {
2665                 return null;
2666             }
2667             else {
2668                 o.conn.open(method, uri, true);
2669
2670                 if (this.useDefaultXhrHeader) {
2671                     if (!this.defaultHeaders['X-Requested-With']) {
2672                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2673                     }
2674                 }
2675
2676                 if(postData && this.useDefaultHeader){
2677                     this.initHeader('Content-Type', this.defaultPostHeader);
2678                 }
2679
2680                  if (this.hasDefaultHeaders || this.hasHeaders) {
2681                     this.setHeader(o);
2682                 }
2683
2684                 this.handleReadyState(o, callback);
2685                 o.conn.send(postData || null);
2686
2687                 return o;
2688             }
2689         },
2690
2691         handleReadyState:function(o, callback)
2692         {
2693             var oConn = this;
2694
2695             if (callback && callback.timeout) {
2696                 this.timeout[o.tId] = window.setTimeout(function() {
2697                     oConn.abort(o, callback, true);
2698                 }, callback.timeout);
2699             }
2700
2701             this.poll[o.tId] = window.setInterval(
2702                     function() {
2703                         if (o.conn && o.conn.readyState == 4) {
2704                             window.clearInterval(oConn.poll[o.tId]);
2705                             delete oConn.poll[o.tId];
2706
2707                             if(callback && callback.timeout) {
2708                                 window.clearTimeout(oConn.timeout[o.tId]);
2709                                 delete oConn.timeout[o.tId];
2710                             }
2711
2712                             oConn.handleTransactionResponse(o, callback);
2713                         }
2714                     }
2715                     , this.pollInterval);
2716         },
2717
2718         handleTransactionResponse:function(o, callback, isAbort)
2719         {
2720
2721             if (!callback) {
2722                 this.releaseObject(o);
2723                 return;
2724             }
2725
2726             var httpStatus, responseObject;
2727
2728             try
2729             {
2730                 if (o.conn.status !== undefined && o.conn.status != 0) {
2731                     httpStatus = o.conn.status;
2732                 }
2733                 else {
2734                     httpStatus = 13030;
2735                 }
2736             }
2737             catch(e) {
2738
2739
2740                 httpStatus = 13030;
2741             }
2742
2743             if (httpStatus >= 200 && httpStatus < 300) {
2744                 responseObject = this.createResponseObject(o, callback.argument);
2745                 if (callback.success) {
2746                     if (!callback.scope) {
2747                         callback.success(responseObject);
2748                     }
2749                     else {
2750
2751
2752                         callback.success.apply(callback.scope, [responseObject]);
2753                     }
2754                 }
2755             }
2756             else {
2757                 switch (httpStatus) {
2758
2759                     case 12002:
2760                     case 12029:
2761                     case 12030:
2762                     case 12031:
2763                     case 12152:
2764                     case 13030:
2765                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2766                         if (callback.failure) {
2767                             if (!callback.scope) {
2768                                 callback.failure(responseObject);
2769                             }
2770                             else {
2771                                 callback.failure.apply(callback.scope, [responseObject]);
2772                             }
2773                         }
2774                         break;
2775                     default:
2776                         responseObject = this.createResponseObject(o, callback.argument);
2777                         if (callback.failure) {
2778                             if (!callback.scope) {
2779                                 callback.failure(responseObject);
2780                             }
2781                             else {
2782                                 callback.failure.apply(callback.scope, [responseObject]);
2783                             }
2784                         }
2785                 }
2786             }
2787
2788             this.releaseObject(o);
2789             responseObject = null;
2790         },
2791
2792         createResponseObject:function(o, callbackArg)
2793         {
2794             var obj = {};
2795             var headerObj = {};
2796
2797             try
2798             {
2799                 var headerStr = o.conn.getAllResponseHeaders();
2800                 var header = headerStr.split('\n');
2801                 for (var i = 0; i < header.length; i++) {
2802                     var delimitPos = header[i].indexOf(':');
2803                     if (delimitPos != -1) {
2804                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2805                     }
2806                 }
2807             }
2808             catch(e) {
2809             }
2810
2811             obj.tId = o.tId;
2812             obj.status = o.conn.status;
2813             obj.statusText = o.conn.statusText;
2814             obj.getResponseHeader = headerObj;
2815             obj.getAllResponseHeaders = headerStr;
2816             obj.responseText = o.conn.responseText;
2817             obj.responseXML = o.conn.responseXML;
2818
2819             if (typeof callbackArg !== undefined) {
2820                 obj.argument = callbackArg;
2821             }
2822
2823             return obj;
2824         },
2825
2826         createExceptionObject:function(tId, callbackArg, isAbort)
2827         {
2828             var COMM_CODE = 0;
2829             var COMM_ERROR = 'communication failure';
2830             var ABORT_CODE = -1;
2831             var ABORT_ERROR = 'transaction aborted';
2832
2833             var obj = {};
2834
2835             obj.tId = tId;
2836             if (isAbort) {
2837                 obj.status = ABORT_CODE;
2838                 obj.statusText = ABORT_ERROR;
2839             }
2840             else {
2841                 obj.status = COMM_CODE;
2842                 obj.statusText = COMM_ERROR;
2843             }
2844
2845             if (callbackArg) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         initHeader:function(label, value, isDefault)
2853         {
2854             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2855
2856             if (headerObj[label] === undefined) {
2857                 headerObj[label] = value;
2858             }
2859             else {
2860
2861
2862                 headerObj[label] = value + "," + headerObj[label];
2863             }
2864
2865             if (isDefault) {
2866                 this.hasDefaultHeaders = true;
2867             }
2868             else {
2869                 this.hasHeaders = true;
2870             }
2871         },
2872
2873
2874         setHeader:function(o)
2875         {
2876             if (this.hasDefaultHeaders) {
2877                 for (var prop in this.defaultHeaders) {
2878                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2879                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2880                     }
2881                 }
2882             }
2883
2884             if (this.hasHeaders) {
2885                 for (var prop in this.headers) {
2886                     if (this.headers.hasOwnProperty(prop)) {
2887                         o.conn.setRequestHeader(prop, this.headers[prop]);
2888                     }
2889                 }
2890                 this.headers = {};
2891                 this.hasHeaders = false;
2892             }
2893         },
2894
2895         resetDefaultHeaders:function() {
2896             delete this.defaultHeaders;
2897             this.defaultHeaders = {};
2898             this.hasDefaultHeaders = false;
2899         },
2900
2901         abort:function(o, callback, isTimeout)
2902         {
2903             if(this.isCallInProgress(o)) {
2904                 o.conn.abort();
2905                 window.clearInterval(this.poll[o.tId]);
2906                 delete this.poll[o.tId];
2907                 if (isTimeout) {
2908                     delete this.timeout[o.tId];
2909                 }
2910
2911                 this.handleTransactionResponse(o, callback, true);
2912
2913                 return true;
2914             }
2915             else {
2916                 return false;
2917             }
2918         },
2919
2920
2921         isCallInProgress:function(o)
2922         {
2923             if (o && o.conn) {
2924                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2925             }
2926             else {
2927
2928                 return false;
2929             }
2930         },
2931
2932
2933         releaseObject:function(o)
2934         {
2935
2936             o.conn = null;
2937
2938             o = null;
2939         },
2940
2941         activeX:[
2942         'MSXML2.XMLHTTP.3.0',
2943         'MSXML2.XMLHTTP',
2944         'Microsoft.XMLHTTP'
2945         ]
2946
2947
2948     };
2949 })();/*
2950  * Portions of this file are based on pieces of Yahoo User Interface Library
2951  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2952  * YUI licensed under the BSD License:
2953  * http://developer.yahoo.net/yui/license.txt
2954  * <script type="text/javascript">
2955  *
2956  */
2957
2958 Roo.lib.Region = function(t, r, b, l) {
2959     this.top = t;
2960     this[1] = t;
2961     this.right = r;
2962     this.bottom = b;
2963     this.left = l;
2964     this[0] = l;
2965 };
2966
2967
2968 Roo.lib.Region.prototype = {
2969     contains : function(region) {
2970         return ( region.left >= this.left &&
2971                  region.right <= this.right &&
2972                  region.top >= this.top &&
2973                  region.bottom <= this.bottom    );
2974
2975     },
2976
2977     getArea : function() {
2978         return ( (this.bottom - this.top) * (this.right - this.left) );
2979     },
2980
2981     intersect : function(region) {
2982         var t = Math.max(this.top, region.top);
2983         var r = Math.min(this.right, region.right);
2984         var b = Math.min(this.bottom, region.bottom);
2985         var l = Math.max(this.left, region.left);
2986
2987         if (b >= t && r >= l) {
2988             return new Roo.lib.Region(t, r, b, l);
2989         } else {
2990             return null;
2991         }
2992     },
2993     union : function(region) {
2994         var t = Math.min(this.top, region.top);
2995         var r = Math.max(this.right, region.right);
2996         var b = Math.max(this.bottom, region.bottom);
2997         var l = Math.min(this.left, region.left);
2998
2999         return new Roo.lib.Region(t, r, b, l);
3000     },
3001
3002     adjust : function(t, l, b, r) {
3003         this.top += t;
3004         this.left += l;
3005         this.right += r;
3006         this.bottom += b;
3007         return this;
3008     }
3009 };
3010
3011 Roo.lib.Region.getRegion = function(el) {
3012     var p = Roo.lib.Dom.getXY(el);
3013
3014     var t = p[1];
3015     var r = p[0] + el.offsetWidth;
3016     var b = p[1] + el.offsetHeight;
3017     var l = p[0];
3018
3019     return new Roo.lib.Region(t, r, b, l);
3020 };
3021 /*
3022  * Portions of this file are based on pieces of Yahoo User Interface Library
3023  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3024  * YUI licensed under the BSD License:
3025  * http://developer.yahoo.net/yui/license.txt
3026  * <script type="text/javascript">
3027  *
3028  */
3029 //@@dep Roo.lib.Region
3030
3031
3032 Roo.lib.Point = function(x, y) {
3033     if (x instanceof Array) {
3034         y = x[1];
3035         x = x[0];
3036     }
3037     this.x = this.right = this.left = this[0] = x;
3038     this.y = this.top = this.bottom = this[1] = y;
3039 };
3040
3041 Roo.lib.Point.prototype = new Roo.lib.Region();
3042 /*
3043  * Portions of this file are based on pieces of Yahoo User Interface Library
3044  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3045  * YUI licensed under the BSD License:
3046  * http://developer.yahoo.net/yui/license.txt
3047  * <script type="text/javascript">
3048  *
3049  */
3050  
3051 (function() {   
3052
3053     Roo.lib.Anim = {
3054         scroll : function(el, args, duration, easing, cb, scope) {
3055             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3056         },
3057
3058         motion : function(el, args, duration, easing, cb, scope) {
3059             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3060         },
3061
3062         color : function(el, args, duration, easing, cb, scope) {
3063             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3064         },
3065
3066         run : function(el, args, duration, easing, cb, scope, type) {
3067             type = type || Roo.lib.AnimBase;
3068             if (typeof easing == "string") {
3069                 easing = Roo.lib.Easing[easing];
3070             }
3071             var anim = new type(el, args, duration, easing);
3072             anim.animateX(function() {
3073                 Roo.callback(cb, scope);
3074             });
3075             return anim;
3076         }
3077     };
3078 })();/*
3079  * Portions of this file are based on pieces of Yahoo User Interface Library
3080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3081  * YUI licensed under the BSD License:
3082  * http://developer.yahoo.net/yui/license.txt
3083  * <script type="text/javascript">
3084  *
3085  */
3086
3087 (function() {    
3088     var libFlyweight;
3089     
3090     function fly(el) {
3091         if (!libFlyweight) {
3092             libFlyweight = new Roo.Element.Flyweight();
3093         }
3094         libFlyweight.dom = el;
3095         return libFlyweight;
3096     }
3097
3098     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3099     
3100    
3101     
3102     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3103         if (el) {
3104             this.init(el, attributes, duration, method);
3105         }
3106     };
3107
3108     Roo.lib.AnimBase.fly = fly;
3109     
3110     
3111     
3112     Roo.lib.AnimBase.prototype = {
3113
3114         toString: function() {
3115             var el = this.getEl();
3116             var id = el.id || el.tagName;
3117             return ("Anim " + id);
3118         },
3119
3120         patterns: {
3121             noNegatives:        /width|height|opacity|padding/i,
3122             offsetAttribute:  /^((width|height)|(top|left))$/,
3123             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3124             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3125         },
3126
3127
3128         doMethod: function(attr, start, end) {
3129             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3130         },
3131
3132
3133         setAttribute: function(attr, val, unit) {
3134             if (this.patterns.noNegatives.test(attr)) {
3135                 val = (val > 0) ? val : 0;
3136             }
3137
3138             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3139         },
3140
3141
3142         getAttribute: function(attr) {
3143             var el = this.getEl();
3144             var val = fly(el).getStyle(attr);
3145
3146             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3147                 return parseFloat(val);
3148             }
3149
3150             var a = this.patterns.offsetAttribute.exec(attr) || [];
3151             var pos = !!( a[3] );
3152             var box = !!( a[2] );
3153
3154
3155             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3156                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3157             } else {
3158                 val = 0;
3159             }
3160
3161             return val;
3162         },
3163
3164
3165         getDefaultUnit: function(attr) {
3166             if (this.patterns.defaultUnit.test(attr)) {
3167                 return 'px';
3168             }
3169
3170             return '';
3171         },
3172
3173         animateX : function(callback, scope) {
3174             var f = function() {
3175                 this.onComplete.removeListener(f);
3176                 if (typeof callback == "function") {
3177                     callback.call(scope || this, this);
3178                 }
3179             };
3180             this.onComplete.addListener(f, this);
3181             this.animate();
3182         },
3183
3184
3185         setRuntimeAttribute: function(attr) {
3186             var start;
3187             var end;
3188             var attributes = this.attributes;
3189
3190             this.runtimeAttributes[attr] = {};
3191
3192             var isset = function(prop) {
3193                 return (typeof prop !== 'undefined');
3194             };
3195
3196             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3197                 return false;
3198             }
3199
3200             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3201
3202
3203             if (isset(attributes[attr]['to'])) {
3204                 end = attributes[attr]['to'];
3205             } else if (isset(attributes[attr]['by'])) {
3206                 if (start.constructor == Array) {
3207                     end = [];
3208                     for (var i = 0, len = start.length; i < len; ++i) {
3209                         end[i] = start[i] + attributes[attr]['by'][i];
3210                     }
3211                 } else {
3212                     end = start + attributes[attr]['by'];
3213                 }
3214             }
3215
3216             this.runtimeAttributes[attr].start = start;
3217             this.runtimeAttributes[attr].end = end;
3218
3219
3220             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3221         },
3222
3223
3224         init: function(el, attributes, duration, method) {
3225
3226             var isAnimated = false;
3227
3228
3229             var startTime = null;
3230
3231
3232             var actualFrames = 0;
3233
3234
3235             el = Roo.getDom(el);
3236
3237
3238             this.attributes = attributes || {};
3239
3240
3241             this.duration = duration || 1;
3242
3243
3244             this.method = method || Roo.lib.Easing.easeNone;
3245
3246
3247             this.useSeconds = true;
3248
3249
3250             this.currentFrame = 0;
3251
3252
3253             this.totalFrames = Roo.lib.AnimMgr.fps;
3254
3255
3256             this.getEl = function() {
3257                 return el;
3258             };
3259
3260
3261             this.isAnimated = function() {
3262                 return isAnimated;
3263             };
3264
3265
3266             this.getStartTime = function() {
3267                 return startTime;
3268             };
3269
3270             this.runtimeAttributes = {};
3271
3272
3273             this.animate = function() {
3274                 if (this.isAnimated()) {
3275                     return false;
3276                 }
3277
3278                 this.currentFrame = 0;
3279
3280                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3281
3282                 Roo.lib.AnimMgr.registerElement(this);
3283             };
3284
3285
3286             this.stop = function(finish) {
3287                 if (finish) {
3288                     this.currentFrame = this.totalFrames;
3289                     this._onTween.fire();
3290                 }
3291                 Roo.lib.AnimMgr.stop(this);
3292             };
3293
3294             var onStart = function() {
3295                 this.onStart.fire();
3296
3297                 this.runtimeAttributes = {};
3298                 for (var attr in this.attributes) {
3299                     this.setRuntimeAttribute(attr);
3300                 }
3301
3302                 isAnimated = true;
3303                 actualFrames = 0;
3304                 startTime = new Date();
3305             };
3306
3307
3308             var onTween = function() {
3309                 var data = {
3310                     duration: new Date() - this.getStartTime(),
3311                     currentFrame: this.currentFrame
3312                 };
3313
3314                 data.toString = function() {
3315                     return (
3316                             'duration: ' + data.duration +
3317                             ', currentFrame: ' + data.currentFrame
3318                             );
3319                 };
3320
3321                 this.onTween.fire(data);
3322
3323                 var runtimeAttributes = this.runtimeAttributes;
3324
3325                 for (var attr in runtimeAttributes) {
3326                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3327                 }
3328
3329                 actualFrames += 1;
3330             };
3331
3332             var onComplete = function() {
3333                 var actual_duration = (new Date() - startTime) / 1000 ;
3334
3335                 var data = {
3336                     duration: actual_duration,
3337                     frames: actualFrames,
3338                     fps: actualFrames / actual_duration
3339                 };
3340
3341                 data.toString = function() {
3342                     return (
3343                             'duration: ' + data.duration +
3344                             ', frames: ' + data.frames +
3345                             ', fps: ' + data.fps
3346                             );
3347                 };
3348
3349                 isAnimated = false;
3350                 actualFrames = 0;
3351                 this.onComplete.fire(data);
3352             };
3353
3354
3355             this._onStart = new Roo.util.Event(this);
3356             this.onStart = new Roo.util.Event(this);
3357             this.onTween = new Roo.util.Event(this);
3358             this._onTween = new Roo.util.Event(this);
3359             this.onComplete = new Roo.util.Event(this);
3360             this._onComplete = new Roo.util.Event(this);
3361             this._onStart.addListener(onStart);
3362             this._onTween.addListener(onTween);
3363             this._onComplete.addListener(onComplete);
3364         }
3365     };
3366 })();
3367 /*
3368  * Portions of this file are based on pieces of Yahoo User Interface Library
3369  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3370  * YUI licensed under the BSD License:
3371  * http://developer.yahoo.net/yui/license.txt
3372  * <script type="text/javascript">
3373  *
3374  */
3375
3376 Roo.lib.AnimMgr = new function() {
3377
3378         var thread = null;
3379
3380
3381         var queue = [];
3382
3383
3384         var tweenCount = 0;
3385
3386
3387         this.fps = 1000;
3388
3389
3390         this.delay = 1;
3391
3392
3393         this.registerElement = function(tween) {
3394             queue[queue.length] = tween;
3395             tweenCount += 1;
3396             tween._onStart.fire();
3397             this.start();
3398         };
3399
3400
3401         this.unRegister = function(tween, index) {
3402             tween._onComplete.fire();
3403             index = index || getIndex(tween);
3404             if (index != -1) {
3405                 queue.splice(index, 1);
3406             }
3407
3408             tweenCount -= 1;
3409             if (tweenCount <= 0) {
3410                 this.stop();
3411             }
3412         };
3413
3414
3415         this.start = function() {
3416             if (thread === null) {
3417                 thread = setInterval(this.run, this.delay);
3418             }
3419         };
3420
3421
3422         this.stop = function(tween) {
3423             if (!tween) {
3424                 clearInterval(thread);
3425
3426                 for (var i = 0, len = queue.length; i < len; ++i) {
3427                     if (queue[0].isAnimated()) {
3428                         this.unRegister(queue[0], 0);
3429                     }
3430                 }
3431
3432                 queue = [];
3433                 thread = null;
3434                 tweenCount = 0;
3435             }
3436             else {
3437                 this.unRegister(tween);
3438             }
3439         };
3440
3441
3442         this.run = function() {
3443             for (var i = 0, len = queue.length; i < len; ++i) {
3444                 var tween = queue[i];
3445                 if (!tween || !tween.isAnimated()) {
3446                     continue;
3447                 }
3448
3449                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3450                 {
3451                     tween.currentFrame += 1;
3452
3453                     if (tween.useSeconds) {
3454                         correctFrame(tween);
3455                     }
3456                     tween._onTween.fire();
3457                 }
3458                 else {
3459                     Roo.lib.AnimMgr.stop(tween, i);
3460                 }
3461             }
3462         };
3463
3464         var getIndex = function(anim) {
3465             for (var i = 0, len = queue.length; i < len; ++i) {
3466                 if (queue[i] == anim) {
3467                     return i;
3468                 }
3469             }
3470             return -1;
3471         };
3472
3473
3474         var correctFrame = function(tween) {
3475             var frames = tween.totalFrames;
3476             var frame = tween.currentFrame;
3477             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3478             var elapsed = (new Date() - tween.getStartTime());
3479             var tweak = 0;
3480
3481             if (elapsed < tween.duration * 1000) {
3482                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3483             } else {
3484                 tweak = frames - (frame + 1);
3485             }
3486             if (tweak > 0 && isFinite(tweak)) {
3487                 if (tween.currentFrame + tweak >= frames) {
3488                     tweak = frames - (frame + 1);
3489                 }
3490
3491                 tween.currentFrame += tweak;
3492             }
3493         };
3494     };/*
3495  * Portions of this file are based on pieces of Yahoo User Interface Library
3496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3497  * YUI licensed under the BSD License:
3498  * http://developer.yahoo.net/yui/license.txt
3499  * <script type="text/javascript">
3500  *
3501  */
3502 Roo.lib.Bezier = new function() {
3503
3504         this.getPosition = function(points, t) {
3505             var n = points.length;
3506             var tmp = [];
3507
3508             for (var i = 0; i < n; ++i) {
3509                 tmp[i] = [points[i][0], points[i][1]];
3510             }
3511
3512             for (var j = 1; j < n; ++j) {
3513                 for (i = 0; i < n - j; ++i) {
3514                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3515                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3516                 }
3517             }
3518
3519             return [ tmp[0][0], tmp[0][1] ];
3520
3521         };
3522     };/*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 (function() {
3531
3532     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3533         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3534     };
3535
3536     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3537
3538     var fly = Roo.lib.AnimBase.fly;
3539     var Y = Roo.lib;
3540     var superclass = Y.ColorAnim.superclass;
3541     var proto = Y.ColorAnim.prototype;
3542
3543     proto.toString = function() {
3544         var el = this.getEl();
3545         var id = el.id || el.tagName;
3546         return ("ColorAnim " + id);
3547     };
3548
3549     proto.patterns.color = /color$/i;
3550     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3551     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3552     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3553     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3554
3555
3556     proto.parseColor = function(s) {
3557         if (s.length == 3) {
3558             return s;
3559         }
3560
3561         var c = this.patterns.hex.exec(s);
3562         if (c && c.length == 4) {
3563             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3564         }
3565
3566         c = this.patterns.rgb.exec(s);
3567         if (c && c.length == 4) {
3568             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3569         }
3570
3571         c = this.patterns.hex3.exec(s);
3572         if (c && c.length == 4) {
3573             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3574         }
3575
3576         return null;
3577     };
3578     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3579     proto.getAttribute = function(attr) {
3580         var el = this.getEl();
3581         if (this.patterns.color.test(attr)) {
3582             var val = fly(el).getStyle(attr);
3583
3584             if (this.patterns.transparent.test(val)) {
3585                 var parent = el.parentNode;
3586                 val = fly(parent).getStyle(attr);
3587
3588                 while (parent && this.patterns.transparent.test(val)) {
3589                     parent = parent.parentNode;
3590                     val = fly(parent).getStyle(attr);
3591                     if (parent.tagName.toUpperCase() == 'HTML') {
3592                         val = '#fff';
3593                     }
3594                 }
3595             }
3596         } else {
3597             val = superclass.getAttribute.call(this, attr);
3598         }
3599
3600         return val;
3601     };
3602     proto.getAttribute = function(attr) {
3603         var el = this.getEl();
3604         if (this.patterns.color.test(attr)) {
3605             var val = fly(el).getStyle(attr);
3606
3607             if (this.patterns.transparent.test(val)) {
3608                 var parent = el.parentNode;
3609                 val = fly(parent).getStyle(attr);
3610
3611                 while (parent && this.patterns.transparent.test(val)) {
3612                     parent = parent.parentNode;
3613                     val = fly(parent).getStyle(attr);
3614                     if (parent.tagName.toUpperCase() == 'HTML') {
3615                         val = '#fff';
3616                     }
3617                 }
3618             }
3619         } else {
3620             val = superclass.getAttribute.call(this, attr);
3621         }
3622
3623         return val;
3624     };
3625
3626     proto.doMethod = function(attr, start, end) {
3627         var val;
3628
3629         if (this.patterns.color.test(attr)) {
3630             val = [];
3631             for (var i = 0, len = start.length; i < len; ++i) {
3632                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3633             }
3634
3635             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3636         }
3637         else {
3638             val = superclass.doMethod.call(this, attr, start, end);
3639         }
3640
3641         return val;
3642     };
3643
3644     proto.setRuntimeAttribute = function(attr) {
3645         superclass.setRuntimeAttribute.call(this, attr);
3646
3647         if (this.patterns.color.test(attr)) {
3648             var attributes = this.attributes;
3649             var start = this.parseColor(this.runtimeAttributes[attr].start);
3650             var end = this.parseColor(this.runtimeAttributes[attr].end);
3651
3652             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3653                 end = this.parseColor(attributes[attr].by);
3654
3655                 for (var i = 0, len = start.length; i < len; ++i) {
3656                     end[i] = start[i] + end[i];
3657                 }
3658             }
3659
3660             this.runtimeAttributes[attr].start = start;
3661             this.runtimeAttributes[attr].end = end;
3662         }
3663     };
3664 })();
3665
3666 /*
3667  * Portions of this file are based on pieces of Yahoo User Interface Library
3668  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3669  * YUI licensed under the BSD License:
3670  * http://developer.yahoo.net/yui/license.txt
3671  * <script type="text/javascript">
3672  *
3673  */
3674 Roo.lib.Easing = {
3675
3676
3677     easeNone: function (t, b, c, d) {
3678         return c * t / d + b;
3679     },
3680
3681
3682     easeIn: function (t, b, c, d) {
3683         return c * (t /= d) * t + b;
3684     },
3685
3686
3687     easeOut: function (t, b, c, d) {
3688         return -c * (t /= d) * (t - 2) + b;
3689     },
3690
3691
3692     easeBoth: function (t, b, c, d) {
3693         if ((t /= d / 2) < 1) {
3694             return c / 2 * t * t + b;
3695         }
3696
3697         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3698     },
3699
3700
3701     easeInStrong: function (t, b, c, d) {
3702         return c * (t /= d) * t * t * t + b;
3703     },
3704
3705
3706     easeOutStrong: function (t, b, c, d) {
3707         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3708     },
3709
3710
3711     easeBothStrong: function (t, b, c, d) {
3712         if ((t /= d / 2) < 1) {
3713             return c / 2 * t * t * t * t + b;
3714         }
3715
3716         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3717     },
3718
3719
3720
3721     elasticIn: function (t, b, c, d, a, p) {
3722         if (t == 0) {
3723             return b;
3724         }
3725         if ((t /= d) == 1) {
3726             return b + c;
3727         }
3728         if (!p) {
3729             p = d * .3;
3730         }
3731
3732         if (!a || a < Math.abs(c)) {
3733             a = c;
3734             var s = p / 4;
3735         }
3736         else {
3737             var s = p / (2 * Math.PI) * Math.asin(c / a);
3738         }
3739
3740         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3741     },
3742
3743
3744     elasticOut: function (t, b, c, d, a, p) {
3745         if (t == 0) {
3746             return b;
3747         }
3748         if ((t /= d) == 1) {
3749             return b + c;
3750         }
3751         if (!p) {
3752             p = d * .3;
3753         }
3754
3755         if (!a || a < Math.abs(c)) {
3756             a = c;
3757             var s = p / 4;
3758         }
3759         else {
3760             var s = p / (2 * Math.PI) * Math.asin(c / a);
3761         }
3762
3763         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3764     },
3765
3766
3767     elasticBoth: function (t, b, c, d, a, p) {
3768         if (t == 0) {
3769             return b;
3770         }
3771
3772         if ((t /= d / 2) == 2) {
3773             return b + c;
3774         }
3775
3776         if (!p) {
3777             p = d * (.3 * 1.5);
3778         }
3779
3780         if (!a || a < Math.abs(c)) {
3781             a = c;
3782             var s = p / 4;
3783         }
3784         else {
3785             var s = p / (2 * Math.PI) * Math.asin(c / a);
3786         }
3787
3788         if (t < 1) {
3789             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3790                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3791         }
3792         return a * Math.pow(2, -10 * (t -= 1)) *
3793                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3794     },
3795
3796
3797
3798     backIn: function (t, b, c, d, s) {
3799         if (typeof s == 'undefined') {
3800             s = 1.70158;
3801         }
3802         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3803     },
3804
3805
3806     backOut: function (t, b, c, d, s) {
3807         if (typeof s == 'undefined') {
3808             s = 1.70158;
3809         }
3810         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3811     },
3812
3813
3814     backBoth: function (t, b, c, d, s) {
3815         if (typeof s == 'undefined') {
3816             s = 1.70158;
3817         }
3818
3819         if ((t /= d / 2 ) < 1) {
3820             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3821         }
3822         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3823     },
3824
3825
3826     bounceIn: function (t, b, c, d) {
3827         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3828     },
3829
3830
3831     bounceOut: function (t, b, c, d) {
3832         if ((t /= d) < (1 / 2.75)) {
3833             return c * (7.5625 * t * t) + b;
3834         } else if (t < (2 / 2.75)) {
3835             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3836         } else if (t < (2.5 / 2.75)) {
3837             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3838         }
3839         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3840     },
3841
3842
3843     bounceBoth: function (t, b, c, d) {
3844         if (t < d / 2) {
3845             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3846         }
3847         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3848     }
3849 };/*
3850  * Portions of this file are based on pieces of Yahoo User Interface Library
3851  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3852  * YUI licensed under the BSD License:
3853  * http://developer.yahoo.net/yui/license.txt
3854  * <script type="text/javascript">
3855  *
3856  */
3857     (function() {
3858         Roo.lib.Motion = function(el, attributes, duration, method) {
3859             if (el) {
3860                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3861             }
3862         };
3863
3864         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3865
3866
3867         var Y = Roo.lib;
3868         var superclass = Y.Motion.superclass;
3869         var proto = Y.Motion.prototype;
3870
3871         proto.toString = function() {
3872             var el = this.getEl();
3873             var id = el.id || el.tagName;
3874             return ("Motion " + id);
3875         };
3876
3877         proto.patterns.points = /^points$/i;
3878
3879         proto.setAttribute = function(attr, val, unit) {
3880             if (this.patterns.points.test(attr)) {
3881                 unit = unit || 'px';
3882                 superclass.setAttribute.call(this, 'left', val[0], unit);
3883                 superclass.setAttribute.call(this, 'top', val[1], unit);
3884             } else {
3885                 superclass.setAttribute.call(this, attr, val, unit);
3886             }
3887         };
3888
3889         proto.getAttribute = function(attr) {
3890             if (this.patterns.points.test(attr)) {
3891                 var val = [
3892                         superclass.getAttribute.call(this, 'left'),
3893                         superclass.getAttribute.call(this, 'top')
3894                         ];
3895             } else {
3896                 val = superclass.getAttribute.call(this, attr);
3897             }
3898
3899             return val;
3900         };
3901
3902         proto.doMethod = function(attr, start, end) {
3903             var val = null;
3904
3905             if (this.patterns.points.test(attr)) {
3906                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3907                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3908             } else {
3909                 val = superclass.doMethod.call(this, attr, start, end);
3910             }
3911             return val;
3912         };
3913
3914         proto.setRuntimeAttribute = function(attr) {
3915             if (this.patterns.points.test(attr)) {
3916                 var el = this.getEl();
3917                 var attributes = this.attributes;
3918                 var start;
3919                 var control = attributes['points']['control'] || [];
3920                 var end;
3921                 var i, len;
3922
3923                 if (control.length > 0 && !(control[0] instanceof Array)) {
3924                     control = [control];
3925                 } else {
3926                     var tmp = [];
3927                     for (i = 0,len = control.length; i < len; ++i) {
3928                         tmp[i] = control[i];
3929                     }
3930                     control = tmp;
3931                 }
3932
3933                 Roo.fly(el).position();
3934
3935                 if (isset(attributes['points']['from'])) {
3936                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3937                 }
3938                 else {
3939                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3940                 }
3941
3942                 start = this.getAttribute('points');
3943
3944
3945                 if (isset(attributes['points']['to'])) {
3946                     end = translateValues.call(this, attributes['points']['to'], start);
3947
3948                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3949                     for (i = 0,len = control.length; i < len; ++i) {
3950                         control[i] = translateValues.call(this, control[i], start);
3951                     }
3952
3953
3954                 } else if (isset(attributes['points']['by'])) {
3955                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3956
3957                     for (i = 0,len = control.length; i < len; ++i) {
3958                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3959                     }
3960                 }
3961
3962                 this.runtimeAttributes[attr] = [start];
3963
3964                 if (control.length > 0) {
3965                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3966                 }
3967
3968                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3969             }
3970             else {
3971                 superclass.setRuntimeAttribute.call(this, attr);
3972             }
3973         };
3974
3975         var translateValues = function(val, start) {
3976             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3978
3979             return val;
3980         };
3981
3982         var isset = function(prop) {
3983             return (typeof prop !== 'undefined');
3984         };
3985     })();
3986 /*
3987  * Portions of this file are based on pieces of Yahoo User Interface Library
3988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3989  * YUI licensed under the BSD License:
3990  * http://developer.yahoo.net/yui/license.txt
3991  * <script type="text/javascript">
3992  *
3993  */
3994     (function() {
3995         Roo.lib.Scroll = function(el, attributes, duration, method) {
3996             if (el) {
3997                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3998             }
3999         };
4000
4001         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4002
4003
4004         var Y = Roo.lib;
4005         var superclass = Y.Scroll.superclass;
4006         var proto = Y.Scroll.prototype;
4007
4008         proto.toString = function() {
4009             var el = this.getEl();
4010             var id = el.id || el.tagName;
4011             return ("Scroll " + id);
4012         };
4013
4014         proto.doMethod = function(attr, start, end) {
4015             var val = null;
4016
4017             if (attr == 'scroll') {
4018                 val = [
4019                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4020                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4021                         ];
4022
4023             } else {
4024                 val = superclass.doMethod.call(this, attr, start, end);
4025             }
4026             return val;
4027         };
4028
4029         proto.getAttribute = function(attr) {
4030             var val = null;
4031             var el = this.getEl();
4032
4033             if (attr == 'scroll') {
4034                 val = [ el.scrollLeft, el.scrollTop ];
4035             } else {
4036                 val = superclass.getAttribute.call(this, attr);
4037             }
4038
4039             return val;
4040         };
4041
4042         proto.setAttribute = function(attr, val, unit) {
4043             var el = this.getEl();
4044
4045             if (attr == 'scroll') {
4046                 el.scrollLeft = val[0];
4047                 el.scrollTop = val[1];
4048             } else {
4049                 superclass.setAttribute.call(this, attr, val, unit);
4050             }
4051         };
4052     })();
4053 /*
4054  * Based on:
4055  * Ext JS Library 1.1.1
4056  * Copyright(c) 2006-2007, Ext JS, LLC.
4057  *
4058  * Originally Released Under LGPL - original licence link has changed is not relivant.
4059  *
4060  * Fork - LGPL
4061  * <script type="text/javascript">
4062  */
4063
4064
4065 // nasty IE9 hack - what a pile of crap that is..
4066
4067  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4068     Range.prototype.createContextualFragment = function (html) {
4069         var doc = window.document;
4070         var container = doc.createElement("div");
4071         container.innerHTML = html;
4072         var frag = doc.createDocumentFragment(), n;
4073         while ((n = container.firstChild)) {
4074             frag.appendChild(n);
4075         }
4076         return frag;
4077     };
4078 }
4079
4080 /**
4081  * @class Roo.DomHelper
4082  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4083  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4084  * @singleton
4085  */
4086 Roo.DomHelper = function(){
4087     var tempTableEl = null;
4088     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4089     var tableRe = /^table|tbody|tr|td$/i;
4090     var xmlns = {};
4091     // build as innerHTML where available
4092     /** @ignore */
4093     var createHtml = function(o){
4094         if(typeof o == 'string'){
4095             return o;
4096         }
4097         var b = "";
4098         if(!o.tag){
4099             o.tag = "div";
4100         }
4101         b += "<" + o.tag;
4102         for(var attr in o){
4103             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4104             if(attr == "style"){
4105                 var s = o["style"];
4106                 if(typeof s == "function"){
4107                     s = s.call();
4108                 }
4109                 if(typeof s == "string"){
4110                     b += ' style="' + s + '"';
4111                 }else if(typeof s == "object"){
4112                     b += ' style="';
4113                     for(var key in s){
4114                         if(typeof s[key] != "function"){
4115                             b += key + ":" + s[key] + ";";
4116                         }
4117                     }
4118                     b += '"';
4119                 }
4120             }else{
4121                 if(attr == "cls"){
4122                     b += ' class="' + o["cls"] + '"';
4123                 }else if(attr == "htmlFor"){
4124                     b += ' for="' + o["htmlFor"] + '"';
4125                 }else{
4126                     b += " " + attr + '="' + o[attr] + '"';
4127                 }
4128             }
4129         }
4130         if(emptyTags.test(o.tag)){
4131             b += "/>";
4132         }else{
4133             b += ">";
4134             var cn = o.children || o.cn;
4135             if(cn){
4136                 //http://bugs.kde.org/show_bug.cgi?id=71506
4137                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4138                     for(var i = 0, len = cn.length; i < len; i++) {
4139                         b += createHtml(cn[i], b);
4140                     }
4141                 }else{
4142                     b += createHtml(cn, b);
4143                 }
4144             }
4145             if(o.html){
4146                 b += o.html;
4147             }
4148             b += "</" + o.tag + ">";
4149         }
4150         return b;
4151     };
4152
4153     // build as dom
4154     /** @ignore */
4155     var createDom = function(o, parentNode){
4156          
4157         // defininition craeted..
4158         var ns = false;
4159         if (o.ns && o.ns != 'html') {
4160                
4161             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4162                 xmlns[o.ns] = o.xmlns;
4163                 ns = o.xmlns;
4164             }
4165             if (typeof(xmlns[o.ns]) == 'undefined') {
4166                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4167             }
4168             ns = xmlns[o.ns];
4169         }
4170         
4171         
4172         if (typeof(o) == 'string') {
4173             return parentNode.appendChild(document.createTextNode(o));
4174         }
4175         o.tag = o.tag || div;
4176         if (o.ns && Roo.isIE) {
4177             ns = false;
4178             o.tag = o.ns + ':' + o.tag;
4179             
4180         }
4181         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4182         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4183         for(var attr in o){
4184             
4185             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4186                     attr == "style" || typeof o[attr] == "function") continue;
4187                     
4188             if(attr=="cls" && Roo.isIE){
4189                 el.className = o["cls"];
4190             }else{
4191                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4192                 else el[attr] = o[attr];
4193             }
4194         }
4195         Roo.DomHelper.applyStyles(el, o.style);
4196         var cn = o.children || o.cn;
4197         if(cn){
4198             //http://bugs.kde.org/show_bug.cgi?id=71506
4199              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4200                 for(var i = 0, len = cn.length; i < len; i++) {
4201                     createDom(cn[i], el);
4202                 }
4203             }else{
4204                 createDom(cn, el);
4205             }
4206         }
4207         if(o.html){
4208             el.innerHTML = o.html;
4209         }
4210         if(parentNode){
4211            parentNode.appendChild(el);
4212         }
4213         return el;
4214     };
4215
4216     var ieTable = function(depth, s, h, e){
4217         tempTableEl.innerHTML = [s, h, e].join('');
4218         var i = -1, el = tempTableEl;
4219         while(++i < depth){
4220             el = el.firstChild;
4221         }
4222         return el;
4223     };
4224
4225     // kill repeat to save bytes
4226     var ts = '<table>',
4227         te = '</table>',
4228         tbs = ts+'<tbody>',
4229         tbe = '</tbody>'+te,
4230         trs = tbs + '<tr>',
4231         tre = '</tr>'+tbe;
4232
4233     /**
4234      * @ignore
4235      * Nasty code for IE's broken table implementation
4236      */
4237     var insertIntoTable = function(tag, where, el, html){
4238         if(!tempTableEl){
4239             tempTableEl = document.createElement('div');
4240         }
4241         var node;
4242         var before = null;
4243         if(tag == 'td'){
4244             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4245                 return;
4246             }
4247             if(where == 'beforebegin'){
4248                 before = el;
4249                 el = el.parentNode;
4250             } else{
4251                 before = el.nextSibling;
4252                 el = el.parentNode;
4253             }
4254             node = ieTable(4, trs, html, tre);
4255         }
4256         else if(tag == 'tr'){
4257             if(where == 'beforebegin'){
4258                 before = el;
4259                 el = el.parentNode;
4260                 node = ieTable(3, tbs, html, tbe);
4261             } else if(where == 'afterend'){
4262                 before = el.nextSibling;
4263                 el = el.parentNode;
4264                 node = ieTable(3, tbs, html, tbe);
4265             } else{ // INTO a TR
4266                 if(where == 'afterbegin'){
4267                     before = el.firstChild;
4268                 }
4269                 node = ieTable(4, trs, html, tre);
4270             }
4271         } else if(tag == 'tbody'){
4272             if(where == 'beforebegin'){
4273                 before = el;
4274                 el = el.parentNode;
4275                 node = ieTable(2, ts, html, te);
4276             } else if(where == 'afterend'){
4277                 before = el.nextSibling;
4278                 el = el.parentNode;
4279                 node = ieTable(2, ts, html, te);
4280             } else{
4281                 if(where == 'afterbegin'){
4282                     before = el.firstChild;
4283                 }
4284                 node = ieTable(3, tbs, html, tbe);
4285             }
4286         } else{ // TABLE
4287             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4288                 return;
4289             }
4290             if(where == 'afterbegin'){
4291                 before = el.firstChild;
4292             }
4293             node = ieTable(2, ts, html, te);
4294         }
4295         el.insertBefore(node, before);
4296         return node;
4297     };
4298
4299     return {
4300     /** True to force the use of DOM instead of html fragments @type Boolean */
4301     useDom : false,
4302
4303     /**
4304      * Returns the markup for the passed Element(s) config
4305      * @param {Object} o The Dom object spec (and children)
4306      * @return {String}
4307      */
4308     markup : function(o){
4309         return createHtml(o);
4310     },
4311
4312     /**
4313      * Applies a style specification to an element
4314      * @param {String/HTMLElement} el The element to apply styles to
4315      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4316      * a function which returns such a specification.
4317      */
4318     applyStyles : function(el, styles){
4319         if(styles){
4320            el = Roo.fly(el);
4321            if(typeof styles == "string"){
4322                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4323                var matches;
4324                while ((matches = re.exec(styles)) != null){
4325                    el.setStyle(matches[1], matches[2]);
4326                }
4327            }else if (typeof styles == "object"){
4328                for (var style in styles){
4329                   el.setStyle(style, styles[style]);
4330                }
4331            }else if (typeof styles == "function"){
4332                 Roo.DomHelper.applyStyles(el, styles.call());
4333            }
4334         }
4335     },
4336
4337     /**
4338      * Inserts an HTML fragment into the Dom
4339      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4340      * @param {HTMLElement} el The context element
4341      * @param {String} html The HTML fragmenet
4342      * @return {HTMLElement} The new node
4343      */
4344     insertHtml : function(where, el, html){
4345         where = where.toLowerCase();
4346         if(el.insertAdjacentHTML){
4347             if(tableRe.test(el.tagName)){
4348                 var rs;
4349                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4350                     return rs;
4351                 }
4352             }
4353             switch(where){
4354                 case "beforebegin":
4355                     el.insertAdjacentHTML('BeforeBegin', html);
4356                     return el.previousSibling;
4357                 case "afterbegin":
4358                     el.insertAdjacentHTML('AfterBegin', html);
4359                     return el.firstChild;
4360                 case "beforeend":
4361                     el.insertAdjacentHTML('BeforeEnd', html);
4362                     return el.lastChild;
4363                 case "afterend":
4364                     el.insertAdjacentHTML('AfterEnd', html);
4365                     return el.nextSibling;
4366             }
4367             throw 'Illegal insertion point -> "' + where + '"';
4368         }
4369         var range = el.ownerDocument.createRange();
4370         var frag;
4371         switch(where){
4372              case "beforebegin":
4373                 range.setStartBefore(el);
4374                 frag = range.createContextualFragment(html);
4375                 el.parentNode.insertBefore(frag, el);
4376                 return el.previousSibling;
4377              case "afterbegin":
4378                 if(el.firstChild){
4379                     range.setStartBefore(el.firstChild);
4380                     frag = range.createContextualFragment(html);
4381                     el.insertBefore(frag, el.firstChild);
4382                     return el.firstChild;
4383                 }else{
4384                     el.innerHTML = html;
4385                     return el.firstChild;
4386                 }
4387             case "beforeend":
4388                 if(el.lastChild){
4389                     range.setStartAfter(el.lastChild);
4390                     frag = range.createContextualFragment(html);
4391                     el.appendChild(frag);
4392                     return el.lastChild;
4393                 }else{
4394                     el.innerHTML = html;
4395                     return el.lastChild;
4396                 }
4397             case "afterend":
4398                 range.setStartAfter(el);
4399                 frag = range.createContextualFragment(html);
4400                 el.parentNode.insertBefore(frag, el.nextSibling);
4401                 return el.nextSibling;
4402             }
4403             throw 'Illegal insertion point -> "' + where + '"';
4404     },
4405
4406     /**
4407      * Creates new Dom element(s) and inserts them before el
4408      * @param {String/HTMLElement/Element} el The context element
4409      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4410      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4411      * @return {HTMLElement/Roo.Element} The new node
4412      */
4413     insertBefore : function(el, o, returnElement){
4414         return this.doInsert(el, o, returnElement, "beforeBegin");
4415     },
4416
4417     /**
4418      * Creates new Dom element(s) and inserts them after el
4419      * @param {String/HTMLElement/Element} el The context element
4420      * @param {Object} o The Dom object spec (and children)
4421      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4422      * @return {HTMLElement/Roo.Element} The new node
4423      */
4424     insertAfter : function(el, o, returnElement){
4425         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4426     },
4427
4428     /**
4429      * Creates new Dom element(s) and inserts them as the first child of el
4430      * @param {String/HTMLElement/Element} el The context element
4431      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4432      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4433      * @return {HTMLElement/Roo.Element} The new node
4434      */
4435     insertFirst : function(el, o, returnElement){
4436         return this.doInsert(el, o, returnElement, "afterBegin");
4437     },
4438
4439     // private
4440     doInsert : function(el, o, returnElement, pos, sibling){
4441         el = Roo.getDom(el);
4442         var newNode;
4443         if(this.useDom || o.ns){
4444             newNode = createDom(o, null);
4445             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4446         }else{
4447             var html = createHtml(o);
4448             newNode = this.insertHtml(pos, el, html);
4449         }
4450         return returnElement ? Roo.get(newNode, true) : newNode;
4451     },
4452
4453     /**
4454      * Creates new Dom element(s) and appends them to el
4455      * @param {String/HTMLElement/Element} el The context element
4456      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4457      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4458      * @return {HTMLElement/Roo.Element} The new node
4459      */
4460     append : function(el, o, returnElement){
4461         el = Roo.getDom(el);
4462         var newNode;
4463         if(this.useDom || o.ns){
4464             newNode = createDom(o, null);
4465             el.appendChild(newNode);
4466         }else{
4467             var html = createHtml(o);
4468             newNode = this.insertHtml("beforeEnd", el, html);
4469         }
4470         return returnElement ? Roo.get(newNode, true) : newNode;
4471     },
4472
4473     /**
4474      * Creates new Dom element(s) and overwrites the contents of el with them
4475      * @param {String/HTMLElement/Element} el The context element
4476      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4477      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4478      * @return {HTMLElement/Roo.Element} The new node
4479      */
4480     overwrite : function(el, o, returnElement){
4481         el = Roo.getDom(el);
4482         if (o.ns) {
4483           
4484             while (el.childNodes.length) {
4485                 el.removeChild(el.firstChild);
4486             }
4487             createDom(o, el);
4488         } else {
4489             el.innerHTML = createHtml(o);   
4490         }
4491         
4492         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4493     },
4494
4495     /**
4496      * Creates a new Roo.DomHelper.Template from the Dom object spec
4497      * @param {Object} o The Dom object spec (and children)
4498      * @return {Roo.DomHelper.Template} The new template
4499      */
4500     createTemplate : function(o){
4501         var html = createHtml(o);
4502         return new Roo.Template(html);
4503     }
4504     };
4505 }();
4506 /*
4507  * Based on:
4508  * Ext JS Library 1.1.1
4509  * Copyright(c) 2006-2007, Ext JS, LLC.
4510  *
4511  * Originally Released Under LGPL - original licence link has changed is not relivant.
4512  *
4513  * Fork - LGPL
4514  * <script type="text/javascript">
4515  */
4516  
4517 /**
4518 * @class Roo.Template
4519 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4520 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4521 * Usage:
4522 <pre><code>
4523 var t = new Roo.Template({
4524     html :  '&lt;div name="{id}"&gt;' + 
4525         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4526         '&lt;/div&gt;',
4527     myformat: function (value, allValues) {
4528         return 'XX' + value;
4529     }
4530 });
4531 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4532 </code></pre>
4533 * For more information see this blog post with examples:
4534 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4535      - Create Elements using DOM, HTML fragments and Templates</a>. 
4536 * @constructor
4537 * @param {Object} cfg - Configuration object.
4538 */
4539 Roo.Template = function(cfg){
4540     // BC!
4541     if(cfg instanceof Array){
4542         cfg = cfg.join("");
4543     }else if(arguments.length > 1){
4544         cfg = Array.prototype.join.call(arguments, "");
4545     }
4546     
4547     
4548     if (typeof(cfg) == 'object') {
4549         Roo.apply(this,cfg)
4550     } else {
4551         // bc
4552         this.html = cfg;
4553     }
4554     if (this.url) {
4555         this.load();
4556     }
4557     
4558 };
4559 Roo.Template.prototype = {
4560     
4561     /**
4562      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4563      *                    it should be fixed so that template is observable...
4564      */
4565     url : false,
4566     /**
4567      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4568      */
4569     html : '',
4570     /**
4571      * Returns an HTML fragment of this template with the specified values applied.
4572      * @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'})
4573      * @return {String} The HTML fragment
4574      */
4575     applyTemplate : function(values){
4576         try {
4577            
4578             if(this.compiled){
4579                 return this.compiled(values);
4580             }
4581             var useF = this.disableFormats !== true;
4582             var fm = Roo.util.Format, tpl = this;
4583             var fn = function(m, name, format, args){
4584                 if(format && useF){
4585                     if(format.substr(0, 5) == "this."){
4586                         return tpl.call(format.substr(5), values[name], values);
4587                     }else{
4588                         if(args){
4589                             // quoted values are required for strings in compiled templates, 
4590                             // but for non compiled we need to strip them
4591                             // quoted reversed for jsmin
4592                             var re = /^\s*['"](.*)["']\s*$/;
4593                             args = args.split(',');
4594                             for(var i = 0, len = args.length; i < len; i++){
4595                                 args[i] = args[i].replace(re, "$1");
4596                             }
4597                             args = [values[name]].concat(args);
4598                         }else{
4599                             args = [values[name]];
4600                         }
4601                         return fm[format].apply(fm, args);
4602                     }
4603                 }else{
4604                     return values[name] !== undefined ? values[name] : "";
4605                 }
4606             };
4607             return this.html.replace(this.re, fn);
4608         } catch (e) {
4609             Roo.log(e);
4610             throw e;
4611         }
4612          
4613     },
4614     
4615     loading : false,
4616       
4617     load : function ()
4618     {
4619          
4620         if (this.loading) {
4621             return;
4622         }
4623         var _t = this;
4624         
4625         this.loading = true;
4626         this.compiled = false;
4627         
4628         var cx = new Roo.data.Connection();
4629         cx.request({
4630             url : this.url,
4631             method : 'GET',
4632             success : function (response) {
4633                 _t.loading = false;
4634                 _t.html = response.responseText;
4635                 _t.url = false;
4636                 _t.compile();
4637              },
4638             failure : function(response) {
4639                 Roo.log("Template failed to load from " + url);
4640                 _t.loading = false;
4641             }
4642         });
4643     },
4644
4645     /**
4646      * Sets the HTML used as the template and optionally compiles it.
4647      * @param {String} html
4648      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4649      * @return {Roo.Template} this
4650      */
4651     set : function(html, compile){
4652         this.html = html;
4653         this.compiled = null;
4654         if(compile){
4655             this.compile();
4656         }
4657         return this;
4658     },
4659     
4660     /**
4661      * True to disable format functions (defaults to false)
4662      * @type Boolean
4663      */
4664     disableFormats : false,
4665     
4666     /**
4667     * The regular expression used to match template variables 
4668     * @type RegExp
4669     * @property 
4670     */
4671     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4672     
4673     /**
4674      * Compiles the template into an internal function, eliminating the RegEx overhead.
4675      * @return {Roo.Template} this
4676      */
4677     compile : function(){
4678         var fm = Roo.util.Format;
4679         var useF = this.disableFormats !== true;
4680         var sep = Roo.isGecko ? "+" : ",";
4681         var fn = function(m, name, format, args){
4682             if(format && useF){
4683                 args = args ? ',' + args : "";
4684                 if(format.substr(0, 5) != "this."){
4685                     format = "fm." + format + '(';
4686                 }else{
4687                     format = 'this.call("'+ format.substr(5) + '", ';
4688                     args = ", values";
4689                 }
4690             }else{
4691                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4692             }
4693             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4694         };
4695         var body;
4696         // branched to use + in gecko and [].join() in others
4697         if(Roo.isGecko){
4698             body = "this.compiled = function(values){ return '" +
4699                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4700                     "';};";
4701         }else{
4702             body = ["this.compiled = function(values){ return ['"];
4703             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4704             body.push("'].join('');};");
4705             body = body.join('');
4706         }
4707         /**
4708          * eval:var:values
4709          * eval:var:fm
4710          */
4711         eval(body);
4712         return this;
4713     },
4714     
4715     // private function used to call members
4716     call : function(fnName, value, allValues){
4717         return this[fnName](value, allValues);
4718     },
4719     
4720     /**
4721      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4722      * @param {String/HTMLElement/Roo.Element} el The context element
4723      * @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'})
4724      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4725      * @return {HTMLElement/Roo.Element} The new node or Element
4726      */
4727     insertFirst: function(el, values, returnElement){
4728         return this.doInsert('afterBegin', el, values, returnElement);
4729     },
4730
4731     /**
4732      * Applies the supplied values to the template and inserts the new node(s) before el.
4733      * @param {String/HTMLElement/Roo.Element} el The context element
4734      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4735      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4736      * @return {HTMLElement/Roo.Element} The new node or Element
4737      */
4738     insertBefore: function(el, values, returnElement){
4739         return this.doInsert('beforeBegin', el, values, returnElement);
4740     },
4741
4742     /**
4743      * Applies the supplied values to the template and inserts the new node(s) after el.
4744      * @param {String/HTMLElement/Roo.Element} el The context element
4745      * @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'})
4746      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4747      * @return {HTMLElement/Roo.Element} The new node or Element
4748      */
4749     insertAfter : function(el, values, returnElement){
4750         return this.doInsert('afterEnd', el, values, returnElement);
4751     },
4752     
4753     /**
4754      * Applies the supplied values to the template and appends the new node(s) to el.
4755      * @param {String/HTMLElement/Roo.Element} el The context element
4756      * @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'})
4757      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4758      * @return {HTMLElement/Roo.Element} The new node or Element
4759      */
4760     append : function(el, values, returnElement){
4761         return this.doInsert('beforeEnd', el, values, returnElement);
4762     },
4763
4764     doInsert : function(where, el, values, returnEl){
4765         el = Roo.getDom(el);
4766         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4767         return returnEl ? Roo.get(newNode, true) : newNode;
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @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'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     overwrite : function(el, values, returnElement){
4778         el = Roo.getDom(el);
4779         el.innerHTML = this.applyTemplate(values);
4780         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4781     }
4782 };
4783 /**
4784  * Alias for {@link #applyTemplate}
4785  * @method
4786  */
4787 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4788
4789 // backwards compat
4790 Roo.DomHelper.Template = Roo.Template;
4791
4792 /**
4793  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4794  * @param {String/HTMLElement} el A DOM element or its id
4795  * @returns {Roo.Template} The created template
4796  * @static
4797  */
4798 Roo.Template.from = function(el){
4799     el = Roo.getDom(el);
4800     return new Roo.Template(el.value || el.innerHTML);
4801 };/*
4802  * Based on:
4803  * Ext JS Library 1.1.1
4804  * Copyright(c) 2006-2007, Ext JS, LLC.
4805  *
4806  * Originally Released Under LGPL - original licence link has changed is not relivant.
4807  *
4808  * Fork - LGPL
4809  * <script type="text/javascript">
4810  */
4811  
4812
4813 /*
4814  * This is code is also distributed under MIT license for use
4815  * with jQuery and prototype JavaScript libraries.
4816  */
4817 /**
4818  * @class Roo.DomQuery
4819 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).
4820 <p>
4821 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>
4822
4823 <p>
4824 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.
4825 </p>
4826 <h4>Element Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>*</b> any element</li>
4829     <li> <b>E</b> an element with the tag E</li>
4830     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4831     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4832     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4833     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4834 </ul>
4835 <h4>Attribute Selectors:</h4>
4836 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4837 <ul class="list">
4838     <li> <b>E[foo]</b> has an attribute "foo"</li>
4839     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4840     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4841     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4842     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4843     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4844     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4845 </ul>
4846 <h4>Pseudo Classes:</h4>
4847 <ul class="list">
4848     <li> <b>E:first-child</b> E is the first child of its parent</li>
4849     <li> <b>E:last-child</b> E is the last child of its parent</li>
4850     <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>
4851     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4852     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4853     <li> <b>E:only-child</b> E is the only child of its parent</li>
4854     <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>
4855     <li> <b>E:first</b> the first E in the resultset</li>
4856     <li> <b>E:last</b> the last E in the resultset</li>
4857     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4858     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4859     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4860     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4861     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4862     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4863     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4864     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4865     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4866 </ul>
4867 <h4>CSS Value Selectors:</h4>
4868 <ul class="list">
4869     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4870     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4871     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4872     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4873     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4874     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4875 </ul>
4876  * @singleton
4877  */
4878 Roo.DomQuery = function(){
4879     var cache = {}, simpleCache = {}, valueCache = {};
4880     var nonSpace = /\S/;
4881     var trimRe = /^\s+|\s+$/g;
4882     var tplRe = /\{(\d+)\}/g;
4883     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4884     var tagTokenRe = /^(#)?([\w-\*]+)/;
4885     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4886
4887     function child(p, index){
4888         var i = 0;
4889         var n = p.firstChild;
4890         while(n){
4891             if(n.nodeType == 1){
4892                if(++i == index){
4893                    return n;
4894                }
4895             }
4896             n = n.nextSibling;
4897         }
4898         return null;
4899     };
4900
4901     function next(n){
4902         while((n = n.nextSibling) && n.nodeType != 1);
4903         return n;
4904     };
4905
4906     function prev(n){
4907         while((n = n.previousSibling) && n.nodeType != 1);
4908         return n;
4909     };
4910
4911     function children(d){
4912         var n = d.firstChild, ni = -1;
4913             while(n){
4914                 var nx = n.nextSibling;
4915                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4916                     d.removeChild(n);
4917                 }else{
4918                     n.nodeIndex = ++ni;
4919                 }
4920                 n = nx;
4921             }
4922             return this;
4923         };
4924
4925     function byClassName(c, a, v){
4926         if(!v){
4927             return c;
4928         }
4929         var r = [], ri = -1, cn;
4930         for(var i = 0, ci; ci = c[i]; i++){
4931             if((' '+ci.className+' ').indexOf(v) != -1){
4932                 r[++ri] = ci;
4933             }
4934         }
4935         return r;
4936     };
4937
4938     function attrValue(n, attr){
4939         if(!n.tagName && typeof n.length != "undefined"){
4940             n = n[0];
4941         }
4942         if(!n){
4943             return null;
4944         }
4945         if(attr == "for"){
4946             return n.htmlFor;
4947         }
4948         if(attr == "class" || attr == "className"){
4949             return n.className;
4950         }
4951         return n.getAttribute(attr) || n[attr];
4952
4953     };
4954
4955     function getNodes(ns, mode, tagName){
4956         var result = [], ri = -1, cs;
4957         if(!ns){
4958             return result;
4959         }
4960         tagName = tagName || "*";
4961         if(typeof ns.getElementsByTagName != "undefined"){
4962             ns = [ns];
4963         }
4964         if(!mode){
4965             for(var i = 0, ni; ni = ns[i]; i++){
4966                 cs = ni.getElementsByTagName(tagName);
4967                 for(var j = 0, ci; ci = cs[j]; j++){
4968                     result[++ri] = ci;
4969                 }
4970             }
4971         }else if(mode == "/" || mode == ">"){
4972             var utag = tagName.toUpperCase();
4973             for(var i = 0, ni, cn; ni = ns[i]; i++){
4974                 cn = ni.children || ni.childNodes;
4975                 for(var j = 0, cj; cj = cn[j]; j++){
4976                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4977                         result[++ri] = cj;
4978                     }
4979                 }
4980             }
4981         }else if(mode == "+"){
4982             var utag = tagName.toUpperCase();
4983             for(var i = 0, n; n = ns[i]; i++){
4984                 while((n = n.nextSibling) && n.nodeType != 1);
4985                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4986                     result[++ri] = n;
4987                 }
4988             }
4989         }else if(mode == "~"){
4990             for(var i = 0, n; n = ns[i]; i++){
4991                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4992                 if(n){
4993                     result[++ri] = n;
4994                 }
4995             }
4996         }
4997         return result;
4998     };
4999
5000     function concat(a, b){
5001         if(b.slice){
5002             return a.concat(b);
5003         }
5004         for(var i = 0, l = b.length; i < l; i++){
5005             a[a.length] = b[i];
5006         }
5007         return a;
5008     }
5009
5010     function byTag(cs, tagName){
5011         if(cs.tagName || cs == document){
5012             cs = [cs];
5013         }
5014         if(!tagName){
5015             return cs;
5016         }
5017         var r = [], ri = -1;
5018         tagName = tagName.toLowerCase();
5019         for(var i = 0, ci; ci = cs[i]; i++){
5020             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byId(cs, attr, id){
5028         if(cs.tagName || cs == document){
5029             cs = [cs];
5030         }
5031         if(!id){
5032             return cs;
5033         }
5034         var r = [], ri = -1;
5035         for(var i = 0,ci; ci = cs[i]; i++){
5036             if(ci && ci.id == id){
5037                 r[++ri] = ci;
5038                 return r;
5039             }
5040         }
5041         return r;
5042     };
5043
5044     function byAttribute(cs, attr, value, op, custom){
5045         var r = [], ri = -1, st = custom=="{";
5046         var f = Roo.DomQuery.operators[op];
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             var a;
5049             if(st){
5050                 a = Roo.DomQuery.getStyle(ci, attr);
5051             }
5052             else if(attr == "class" || attr == "className"){
5053                 a = ci.className;
5054             }else if(attr == "for"){
5055                 a = ci.htmlFor;
5056             }else if(attr == "href"){
5057                 a = ci.getAttribute("href", 2);
5058             }else{
5059                 a = ci.getAttribute(attr);
5060             }
5061             if((f && f(a, value)) || (!f && a)){
5062                 r[++ri] = ci;
5063             }
5064         }
5065         return r;
5066     };
5067
5068     function byPseudo(cs, name, value){
5069         return Roo.DomQuery.pseudos[name](cs, value);
5070     };
5071
5072     // This is for IE MSXML which does not support expandos.
5073     // IE runs the same speed using setAttribute, however FF slows way down
5074     // and Safari completely fails so they need to continue to use expandos.
5075     var isIE = window.ActiveXObject ? true : false;
5076
5077     // this eval is stop the compressor from
5078     // renaming the variable to something shorter
5079     
5080     /** eval:var:batch */
5081     var batch = 30803; 
5082
5083     var key = 30803;
5084
5085     function nodupIEXml(cs){
5086         var d = ++key;
5087         cs[0].setAttribute("_nodup", d);
5088         var r = [cs[0]];
5089         for(var i = 1, len = cs.length; i < len; i++){
5090             var c = cs[i];
5091             if(!c.getAttribute("_nodup") != d){
5092                 c.setAttribute("_nodup", d);
5093                 r[r.length] = c;
5094             }
5095         }
5096         for(var i = 0, len = cs.length; i < len; i++){
5097             cs[i].removeAttribute("_nodup");
5098         }
5099         return r;
5100     }
5101
5102     function nodup(cs){
5103         if(!cs){
5104             return [];
5105         }
5106         var len = cs.length, c, i, r = cs, cj, ri = -1;
5107         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5108             return cs;
5109         }
5110         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5111             return nodupIEXml(cs);
5112         }
5113         var d = ++key;
5114         cs[0]._nodup = d;
5115         for(i = 1; c = cs[i]; i++){
5116             if(c._nodup != d){
5117                 c._nodup = d;
5118             }else{
5119                 r = [];
5120                 for(var j = 0; j < i; j++){
5121                     r[++ri] = cs[j];
5122                 }
5123                 for(j = i+1; cj = cs[j]; j++){
5124                     if(cj._nodup != d){
5125                         cj._nodup = d;
5126                         r[++ri] = cj;
5127                     }
5128                 }
5129                 return r;
5130             }
5131         }
5132         return r;
5133     }
5134
5135     function quickDiffIEXml(c1, c2){
5136         var d = ++key;
5137         for(var i = 0, len = c1.length; i < len; i++){
5138             c1[i].setAttribute("_qdiff", d);
5139         }
5140         var r = [];
5141         for(var i = 0, len = c2.length; i < len; i++){
5142             if(c2[i].getAttribute("_qdiff") != d){
5143                 r[r.length] = c2[i];
5144             }
5145         }
5146         for(var i = 0, len = c1.length; i < len; i++){
5147            c1[i].removeAttribute("_qdiff");
5148         }
5149         return r;
5150     }
5151
5152     function quickDiff(c1, c2){
5153         var len1 = c1.length;
5154         if(!len1){
5155             return c2;
5156         }
5157         if(isIE && c1[0].selectSingleNode){
5158             return quickDiffIEXml(c1, c2);
5159         }
5160         var d = ++key;
5161         for(var i = 0; i < len1; i++){
5162             c1[i]._qdiff = d;
5163         }
5164         var r = [];
5165         for(var i = 0, len = c2.length; i < len; i++){
5166             if(c2[i]._qdiff != d){
5167                 r[r.length] = c2[i];
5168             }
5169         }
5170         return r;
5171     }
5172
5173     function quickId(ns, mode, root, id){
5174         if(ns == root){
5175            var d = root.ownerDocument || root;
5176            return d.getElementById(id);
5177         }
5178         ns = getNodes(ns, mode, "*");
5179         return byId(ns, null, id);
5180     }
5181
5182     return {
5183         getStyle : function(el, name){
5184             return Roo.fly(el).getStyle(name);
5185         },
5186         /**
5187          * Compiles a selector/xpath query into a reusable function. The returned function
5188          * takes one parameter "root" (optional), which is the context node from where the query should start.
5189          * @param {String} selector The selector/xpath query
5190          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5191          * @return {Function}
5192          */
5193         compile : function(path, type){
5194             type = type || "select";
5195             
5196             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5197             var q = path, mode, lq;
5198             var tk = Roo.DomQuery.matchers;
5199             var tklen = tk.length;
5200             var mm;
5201
5202             // accept leading mode switch
5203             var lmode = q.match(modeRe);
5204             if(lmode && lmode[1]){
5205                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5206                 q = q.replace(lmode[1], "");
5207             }
5208             // strip leading slashes
5209             while(path.substr(0, 1)=="/"){
5210                 path = path.substr(1);
5211             }
5212
5213             while(q && lq != q){
5214                 lq = q;
5215                 var tm = q.match(tagTokenRe);
5216                 if(type == "select"){
5217                     if(tm){
5218                         if(tm[1] == "#"){
5219                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5220                         }else{
5221                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5222                         }
5223                         q = q.replace(tm[0], "");
5224                     }else if(q.substr(0, 1) != '@'){
5225                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5226                     }
5227                 }else{
5228                     if(tm){
5229                         if(tm[1] == "#"){
5230                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5231                         }else{
5232                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5233                         }
5234                         q = q.replace(tm[0], "");
5235                     }
5236                 }
5237                 while(!(mm = q.match(modeRe))){
5238                     var matched = false;
5239                     for(var j = 0; j < tklen; j++){
5240                         var t = tk[j];
5241                         var m = q.match(t.re);
5242                         if(m){
5243                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5244                                                     return m[i];
5245                                                 });
5246                             q = q.replace(m[0], "");
5247                             matched = true;
5248                             break;
5249                         }
5250                     }
5251                     // prevent infinite loop on bad selector
5252                     if(!matched){
5253                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5254                     }
5255                 }
5256                 if(mm[1]){
5257                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5258                     q = q.replace(mm[1], "");
5259                 }
5260             }
5261             fn[fn.length] = "return nodup(n);\n}";
5262             
5263              /** 
5264               * list of variables that need from compression as they are used by eval.
5265              *  eval:var:batch 
5266              *  eval:var:nodup
5267              *  eval:var:byTag
5268              *  eval:var:ById
5269              *  eval:var:getNodes
5270              *  eval:var:quickId
5271              *  eval:var:mode
5272              *  eval:var:root
5273              *  eval:var:n
5274              *  eval:var:byClassName
5275              *  eval:var:byPseudo
5276              *  eval:var:byAttribute
5277              *  eval:var:attrValue
5278              * 
5279              **/ 
5280             eval(fn.join(""));
5281             return f;
5282         },
5283
5284         /**
5285          * Selects a group of elements.
5286          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5287          * @param {Node} root (optional) The start of the query (defaults to document).
5288          * @return {Array}
5289          */
5290         select : function(path, root, type){
5291             if(!root || root == document){
5292                 root = document;
5293             }
5294             if(typeof root == "string"){
5295                 root = document.getElementById(root);
5296             }
5297             var paths = path.split(",");
5298             var results = [];
5299             for(var i = 0, len = paths.length; i < len; i++){
5300                 var p = paths[i].replace(trimRe, "");
5301                 if(!cache[p]){
5302                     cache[p] = Roo.DomQuery.compile(p);
5303                     if(!cache[p]){
5304                         throw p + " is not a valid selector";
5305                     }
5306                 }
5307                 var result = cache[p](root);
5308                 if(result && result != document){
5309                     results = results.concat(result);
5310                 }
5311             }
5312             if(paths.length > 1){
5313                 return nodup(results);
5314             }
5315             return results;
5316         },
5317
5318         /**
5319          * Selects a single element.
5320          * @param {String} selector The selector/xpath query
5321          * @param {Node} root (optional) The start of the query (defaults to document).
5322          * @return {Element}
5323          */
5324         selectNode : function(path, root){
5325             return Roo.DomQuery.select(path, root)[0];
5326         },
5327
5328         /**
5329          * Selects the value of a node, optionally replacing null with the defaultValue.
5330          * @param {String} selector The selector/xpath query
5331          * @param {Node} root (optional) The start of the query (defaults to document).
5332          * @param {String} defaultValue
5333          */
5334         selectValue : function(path, root, defaultValue){
5335             path = path.replace(trimRe, "");
5336             if(!valueCache[path]){
5337                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5338             }
5339             var n = valueCache[path](root);
5340             n = n[0] ? n[0] : n;
5341             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5342             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5343         },
5344
5345         /**
5346          * Selects the value of a node, parsing integers and floats.
5347          * @param {String} selector The selector/xpath query
5348          * @param {Node} root (optional) The start of the query (defaults to document).
5349          * @param {Number} defaultValue
5350          * @return {Number}
5351          */
5352         selectNumber : function(path, root, defaultValue){
5353             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5354             return parseFloat(v);
5355         },
5356
5357         /**
5358          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5359          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5360          * @param {String} selector The simple selector to test
5361          * @return {Boolean}
5362          */
5363         is : function(el, ss){
5364             if(typeof el == "string"){
5365                 el = document.getElementById(el);
5366             }
5367             var isArray = (el instanceof Array);
5368             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5369             return isArray ? (result.length == el.length) : (result.length > 0);
5370         },
5371
5372         /**
5373          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5374          * @param {Array} el An array of elements to filter
5375          * @param {String} selector The simple selector to test
5376          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5377          * the selector instead of the ones that match
5378          * @return {Array}
5379          */
5380         filter : function(els, ss, nonMatches){
5381             ss = ss.replace(trimRe, "");
5382             if(!simpleCache[ss]){
5383                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5384             }
5385             var result = simpleCache[ss](els);
5386             return nonMatches ? quickDiff(result, els) : result;
5387         },
5388
5389         /**
5390          * Collection of matching regular expressions and code snippets.
5391          */
5392         matchers : [{
5393                 re: /^\.([\w-]+)/,
5394                 select: 'n = byClassName(n, null, " {1} ");'
5395             }, {
5396                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5397                 select: 'n = byPseudo(n, "{1}", "{2}");'
5398             },{
5399                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5400                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5401             }, {
5402                 re: /^#([\w-]+)/,
5403                 select: 'n = byId(n, null, "{1}");'
5404             },{
5405                 re: /^@([\w-]+)/,
5406                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5407             }
5408         ],
5409
5410         /**
5411          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5412          * 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;.
5413          */
5414         operators : {
5415             "=" : function(a, v){
5416                 return a == v;
5417             },
5418             "!=" : function(a, v){
5419                 return a != v;
5420             },
5421             "^=" : function(a, v){
5422                 return a && a.substr(0, v.length) == v;
5423             },
5424             "$=" : function(a, v){
5425                 return a && a.substr(a.length-v.length) == v;
5426             },
5427             "*=" : function(a, v){
5428                 return a && a.indexOf(v) !== -1;
5429             },
5430             "%=" : function(a, v){
5431                 return (a % v) == 0;
5432             },
5433             "|=" : function(a, v){
5434                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5435             },
5436             "~=" : function(a, v){
5437                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5438             }
5439         },
5440
5441         /**
5442          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5443          * and the argument (if any) supplied in the selector.
5444          */
5445         pseudos : {
5446             "first-child" : function(c){
5447                 var r = [], ri = -1, n;
5448                 for(var i = 0, ci; ci = n = c[i]; i++){
5449                     while((n = n.previousSibling) && n.nodeType != 1);
5450                     if(!n){
5451                         r[++ri] = ci;
5452                     }
5453                 }
5454                 return r;
5455             },
5456
5457             "last-child" : function(c){
5458                 var r = [], ri = -1, n;
5459                 for(var i = 0, ci; ci = n = c[i]; i++){
5460                     while((n = n.nextSibling) && n.nodeType != 1);
5461                     if(!n){
5462                         r[++ri] = ci;
5463                     }
5464                 }
5465                 return r;
5466             },
5467
5468             "nth-child" : function(c, a) {
5469                 var r = [], ri = -1;
5470                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5471                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5472                 for(var i = 0, n; n = c[i]; i++){
5473                     var pn = n.parentNode;
5474                     if (batch != pn._batch) {
5475                         var j = 0;
5476                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5477                             if(cn.nodeType == 1){
5478                                cn.nodeIndex = ++j;
5479                             }
5480                         }
5481                         pn._batch = batch;
5482                     }
5483                     if (f == 1) {
5484                         if (l == 0 || n.nodeIndex == l){
5485                             r[++ri] = n;
5486                         }
5487                     } else if ((n.nodeIndex + l) % f == 0){
5488                         r[++ri] = n;
5489                     }
5490                 }
5491
5492                 return r;
5493             },
5494
5495             "only-child" : function(c){
5496                 var r = [], ri = -1;;
5497                 for(var i = 0, ci; ci = c[i]; i++){
5498                     if(!prev(ci) && !next(ci)){
5499                         r[++ri] = ci;
5500                     }
5501                 }
5502                 return r;
5503             },
5504
5505             "empty" : function(c){
5506                 var r = [], ri = -1;
5507                 for(var i = 0, ci; ci = c[i]; i++){
5508                     var cns = ci.childNodes, j = 0, cn, empty = true;
5509                     while(cn = cns[j]){
5510                         ++j;
5511                         if(cn.nodeType == 1 || cn.nodeType == 3){
5512                             empty = false;
5513                             break;
5514                         }
5515                     }
5516                     if(empty){
5517                         r[++ri] = ci;
5518                     }
5519                 }
5520                 return r;
5521             },
5522
5523             "contains" : function(c, v){
5524                 var r = [], ri = -1;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "nodeValue" : function(c, v){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5537                         r[++ri] = ci;
5538                     }
5539                 }
5540                 return r;
5541             },
5542
5543             "checked" : function(c){
5544                 var r = [], ri = -1;
5545                 for(var i = 0, ci; ci = c[i]; i++){
5546                     if(ci.checked == true){
5547                         r[++ri] = ci;
5548                     }
5549                 }
5550                 return r;
5551             },
5552
5553             "not" : function(c, ss){
5554                 return Roo.DomQuery.filter(c, ss, true);
5555             },
5556
5557             "odd" : function(c){
5558                 return this["nth-child"](c, "odd");
5559             },
5560
5561             "even" : function(c){
5562                 return this["nth-child"](c, "even");
5563             },
5564
5565             "nth" : function(c, a){
5566                 return c[a-1] || [];
5567             },
5568
5569             "first" : function(c){
5570                 return c[0] || [];
5571             },
5572
5573             "last" : function(c){
5574                 return c[c.length-1] || [];
5575             },
5576
5577             "has" : function(c, ss){
5578                 var s = Roo.DomQuery.select;
5579                 var r = [], ri = -1;
5580                 for(var i = 0, ci; ci = c[i]; i++){
5581                     if(s(ss, ci).length > 0){
5582                         r[++ri] = ci;
5583                     }
5584                 }
5585                 return r;
5586             },
5587
5588             "next" : function(c, ss){
5589                 var is = Roo.DomQuery.is;
5590                 var r = [], ri = -1;
5591                 for(var i = 0, ci; ci = c[i]; i++){
5592                     var n = next(ci);
5593                     if(n && is(n, ss)){
5594                         r[++ri] = ci;
5595                     }
5596                 }
5597                 return r;
5598             },
5599
5600             "prev" : function(c, ss){
5601                 var is = Roo.DomQuery.is;
5602                 var r = [], ri = -1;
5603                 for(var i = 0, ci; ci = c[i]; i++){
5604                     var n = prev(ci);
5605                     if(n && is(n, ss)){
5606                         r[++ri] = ci;
5607                     }
5608                 }
5609                 return r;
5610             }
5611         }
5612     };
5613 }();
5614
5615 /**
5616  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5617  * @param {String} path The selector/xpath query
5618  * @param {Node} root (optional) The start of the query (defaults to document).
5619  * @return {Array}
5620  * @member Roo
5621  * @method query
5622  */
5623 Roo.query = Roo.DomQuery.select;
5624 /*
5625  * Based on:
5626  * Ext JS Library 1.1.1
5627  * Copyright(c) 2006-2007, Ext JS, LLC.
5628  *
5629  * Originally Released Under LGPL - original licence link has changed is not relivant.
5630  *
5631  * Fork - LGPL
5632  * <script type="text/javascript">
5633  */
5634
5635 /**
5636  * @class Roo.util.Observable
5637  * Base class that provides a common interface for publishing events. Subclasses are expected to
5638  * to have a property "events" with all the events defined.<br>
5639  * For example:
5640  * <pre><code>
5641  Employee = function(name){
5642     this.name = name;
5643     this.addEvents({
5644         "fired" : true,
5645         "quit" : true
5646     });
5647  }
5648  Roo.extend(Employee, Roo.util.Observable);
5649 </code></pre>
5650  * @param {Object} config properties to use (incuding events / listeners)
5651  */
5652
5653 Roo.util.Observable = function(cfg){
5654     
5655     cfg = cfg|| {};
5656     this.addEvents(cfg.events || {});
5657     if (cfg.events) {
5658         delete cfg.events; // make sure
5659     }
5660      
5661     Roo.apply(this, cfg);
5662     
5663     if(this.listeners){
5664         this.on(this.listeners);
5665         delete this.listeners;
5666     }
5667 };
5668 Roo.util.Observable.prototype = {
5669     /** 
5670  * @cfg {Object} listeners  list of events and functions to call for this object, 
5671  * For example :
5672  * <pre><code>
5673     listeners :  { 
5674        'click' : function(e) {
5675            ..... 
5676         } ,
5677         .... 
5678     } 
5679   </code></pre>
5680  */
5681     
5682     
5683     /**
5684      * Fires the specified event with the passed parameters (minus the event name).
5685      * @param {String} eventName
5686      * @param {Object...} args Variable number of parameters are passed to handlers
5687      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5688      */
5689     fireEvent : function(){
5690         var ce = this.events[arguments[0].toLowerCase()];
5691         if(typeof ce == "object"){
5692             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5693         }else{
5694             return true;
5695         }
5696     },
5697
5698     // private
5699     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5700
5701     /**
5702      * Appends an event handler to this component
5703      * @param {String}   eventName The type of event to listen for
5704      * @param {Function} handler The method the event invokes
5705      * @param {Object}   scope (optional) The scope in which to execute the handler
5706      * function. The handler function's "this" context.
5707      * @param {Object}   options (optional) An object containing handler configuration
5708      * properties. This may contain any of the following properties:<ul>
5709      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5710      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5711      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5712      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5713      * by the specified number of milliseconds. If the event fires again within that time, the original
5714      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5715      * </ul><br>
5716      * <p>
5717      * <b>Combining Options</b><br>
5718      * Using the options argument, it is possible to combine different types of listeners:<br>
5719      * <br>
5720      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5721                 <pre><code>
5722                 el.on('click', this.onClick, this, {
5723                         single: true,
5724                 delay: 100,
5725                 forumId: 4
5726                 });
5727                 </code></pre>
5728      * <p>
5729      * <b>Attaching multiple handlers in 1 call</b><br>
5730      * The method also allows for a single argument to be passed which is a config object containing properties
5731      * which specify multiple handlers.
5732      * <pre><code>
5733                 el.on({
5734                         'click': {
5735                         fn: this.onClick,
5736                         scope: this,
5737                         delay: 100
5738                 }, 
5739                 'mouseover': {
5740                         fn: this.onMouseOver,
5741                         scope: this
5742                 },
5743                 'mouseout': {
5744                         fn: this.onMouseOut,
5745                         scope: this
5746                 }
5747                 });
5748                 </code></pre>
5749      * <p>
5750      * Or a shorthand syntax which passes the same scope object to all handlers:
5751         <pre><code>
5752                 el.on({
5753                         'click': this.onClick,
5754                 'mouseover': this.onMouseOver,
5755                 'mouseout': this.onMouseOut,
5756                 scope: this
5757                 });
5758                 </code></pre>
5759      */
5760     addListener : function(eventName, fn, scope, o){
5761         if(typeof eventName == "object"){
5762             o = eventName;
5763             for(var e in o){
5764                 if(this.filterOptRe.test(e)){
5765                     continue;
5766                 }
5767                 if(typeof o[e] == "function"){
5768                     // shared options
5769                     this.addListener(e, o[e], o.scope,  o);
5770                 }else{
5771                     // individual options
5772                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5773                 }
5774             }
5775             return;
5776         }
5777         o = (!o || typeof o == "boolean") ? {} : o;
5778         eventName = eventName.toLowerCase();
5779         var ce = this.events[eventName] || true;
5780         if(typeof ce == "boolean"){
5781             ce = new Roo.util.Event(this, eventName);
5782             this.events[eventName] = ce;
5783         }
5784         ce.addListener(fn, scope, o);
5785     },
5786
5787     /**
5788      * Removes a listener
5789      * @param {String}   eventName     The type of event to listen for
5790      * @param {Function} handler        The handler to remove
5791      * @param {Object}   scope  (optional) The scope (this object) for the handler
5792      */
5793     removeListener : function(eventName, fn, scope){
5794         var ce = this.events[eventName.toLowerCase()];
5795         if(typeof ce == "object"){
5796             ce.removeListener(fn, scope);
5797         }
5798     },
5799
5800     /**
5801      * Removes all listeners for this object
5802      */
5803     purgeListeners : function(){
5804         for(var evt in this.events){
5805             if(typeof this.events[evt] == "object"){
5806                  this.events[evt].clearListeners();
5807             }
5808         }
5809     },
5810
5811     relayEvents : function(o, events){
5812         var createHandler = function(ename){
5813             return function(){
5814                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5815             };
5816         };
5817         for(var i = 0, len = events.length; i < len; i++){
5818             var ename = events[i];
5819             if(!this.events[ename]){ this.events[ename] = true; };
5820             o.on(ename, createHandler(ename), this);
5821         }
5822     },
5823
5824     /**
5825      * Used to define events on this Observable
5826      * @param {Object} object The object with the events defined
5827      */
5828     addEvents : function(o){
5829         if(!this.events){
5830             this.events = {};
5831         }
5832         Roo.applyIf(this.events, o);
5833     },
5834
5835     /**
5836      * Checks to see if this object has any listeners for a specified event
5837      * @param {String} eventName The name of the event to check for
5838      * @return {Boolean} True if the event is being listened for, else false
5839      */
5840     hasListener : function(eventName){
5841         var e = this.events[eventName];
5842         return typeof e == "object" && e.listeners.length > 0;
5843     }
5844 };
5845 /**
5846  * Appends an event handler to this element (shorthand for addListener)
5847  * @param {String}   eventName     The type of event to listen for
5848  * @param {Function} handler        The method the event invokes
5849  * @param {Object}   scope (optional) The scope in which to execute the handler
5850  * function. The handler function's "this" context.
5851  * @param {Object}   options  (optional)
5852  * @method
5853  */
5854 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5855 /**
5856  * Removes a listener (shorthand for removeListener)
5857  * @param {String}   eventName     The type of event to listen for
5858  * @param {Function} handler        The handler to remove
5859  * @param {Object}   scope  (optional) The scope (this object) for the handler
5860  * @method
5861  */
5862 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5863
5864 /**
5865  * Starts capture on the specified Observable. All events will be passed
5866  * to the supplied function with the event name + standard signature of the event
5867  * <b>before</b> the event is fired. If the supplied function returns false,
5868  * the event will not fire.
5869  * @param {Observable} o The Observable to capture
5870  * @param {Function} fn The function to call
5871  * @param {Object} scope (optional) The scope (this object) for the fn
5872  * @static
5873  */
5874 Roo.util.Observable.capture = function(o, fn, scope){
5875     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5876 };
5877
5878 /**
5879  * Removes <b>all</b> added captures from the Observable.
5880  * @param {Observable} o The Observable to release
5881  * @static
5882  */
5883 Roo.util.Observable.releaseCapture = function(o){
5884     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5885 };
5886
5887 (function(){
5888
5889     var createBuffered = function(h, o, scope){
5890         var task = new Roo.util.DelayedTask();
5891         return function(){
5892             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5893         };
5894     };
5895
5896     var createSingle = function(h, e, fn, scope){
5897         return function(){
5898             e.removeListener(fn, scope);
5899             return h.apply(scope, arguments);
5900         };
5901     };
5902
5903     var createDelayed = function(h, o, scope){
5904         return function(){
5905             var args = Array.prototype.slice.call(arguments, 0);
5906             setTimeout(function(){
5907                 h.apply(scope, args);
5908             }, o.delay || 10);
5909         };
5910     };
5911
5912     Roo.util.Event = function(obj, name){
5913         this.name = name;
5914         this.obj = obj;
5915         this.listeners = [];
5916     };
5917
5918     Roo.util.Event.prototype = {
5919         addListener : function(fn, scope, options){
5920             var o = options || {};
5921             scope = scope || this.obj;
5922             if(!this.isListening(fn, scope)){
5923                 var l = {fn: fn, scope: scope, options: o};
5924                 var h = fn;
5925                 if(o.delay){
5926                     h = createDelayed(h, o, scope);
5927                 }
5928                 if(o.single){
5929                     h = createSingle(h, this, fn, scope);
5930                 }
5931                 if(o.buffer){
5932                     h = createBuffered(h, o, scope);
5933                 }
5934                 l.fireFn = h;
5935                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5936                     this.listeners.push(l);
5937                 }else{
5938                     this.listeners = this.listeners.slice(0);
5939                     this.listeners.push(l);
5940                 }
5941             }
5942         },
5943
5944         findListener : function(fn, scope){
5945             scope = scope || this.obj;
5946             var ls = this.listeners;
5947             for(var i = 0, len = ls.length; i < len; i++){
5948                 var l = ls[i];
5949                 if(l.fn == fn && l.scope == scope){
5950                     return i;
5951                 }
5952             }
5953             return -1;
5954         },
5955
5956         isListening : function(fn, scope){
5957             return this.findListener(fn, scope) != -1;
5958         },
5959
5960         removeListener : function(fn, scope){
5961             var index;
5962             if((index = this.findListener(fn, scope)) != -1){
5963                 if(!this.firing){
5964                     this.listeners.splice(index, 1);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.splice(index, 1);
5968                 }
5969                 return true;
5970             }
5971             return false;
5972         },
5973
5974         clearListeners : function(){
5975             this.listeners = [];
5976         },
5977
5978         fire : function(){
5979             var ls = this.listeners, scope, len = ls.length;
5980             if(len > 0){
5981                 this.firing = true;
5982                 var args = Array.prototype.slice.call(arguments, 0);
5983                 for(var i = 0; i < len; i++){
5984                     var l = ls[i];
5985                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5986                         this.firing = false;
5987                         return false;
5988                     }
5989                 }
5990                 this.firing = false;
5991             }
5992             return true;
5993         }
5994     };
5995 })();/*
5996  * Based on:
5997  * Ext JS Library 1.1.1
5998  * Copyright(c) 2006-2007, Ext JS, LLC.
5999  *
6000  * Originally Released Under LGPL - original licence link has changed is not relivant.
6001  *
6002  * Fork - LGPL
6003  * <script type="text/javascript">
6004  */
6005
6006 /**
6007  * @class Roo.EventManager
6008  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6009  * several useful events directly.
6010  * See {@link Roo.EventObject} for more details on normalized event objects.
6011  * @singleton
6012  */
6013 Roo.EventManager = function(){
6014     var docReadyEvent, docReadyProcId, docReadyState = false;
6015     var resizeEvent, resizeTask, textEvent, textSize;
6016     var E = Roo.lib.Event;
6017     var D = Roo.lib.Dom;
6018
6019
6020     var fireDocReady = function(){
6021         if(!docReadyState){
6022             docReadyState = true;
6023             Roo.isReady = true;
6024             if(docReadyProcId){
6025                 clearInterval(docReadyProcId);
6026             }
6027             if(Roo.isGecko || Roo.isOpera) {
6028                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6029             }
6030             if(Roo.isIE){
6031                 var defer = document.getElementById("ie-deferred-loader");
6032                 if(defer){
6033                     defer.onreadystatechange = null;
6034                     defer.parentNode.removeChild(defer);
6035                 }
6036             }
6037             if(docReadyEvent){
6038                 docReadyEvent.fire();
6039                 docReadyEvent.clearListeners();
6040             }
6041         }
6042     };
6043     
6044     var initDocReady = function(){
6045         docReadyEvent = new Roo.util.Event();
6046         if(Roo.isGecko || Roo.isOpera) {
6047             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6048         }else if(Roo.isIE){
6049             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6050             var defer = document.getElementById("ie-deferred-loader");
6051             defer.onreadystatechange = function(){
6052                 if(this.readyState == "complete"){
6053                     fireDocReady();
6054                 }
6055             };
6056         }else if(Roo.isSafari){ 
6057             docReadyProcId = setInterval(function(){
6058                 var rs = document.readyState;
6059                 if(rs == "complete") {
6060                     fireDocReady();     
6061                  }
6062             }, 10);
6063         }
6064         // no matter what, make sure it fires on load
6065         E.on(window, "load", fireDocReady);
6066     };
6067
6068     var createBuffered = function(h, o){
6069         var task = new Roo.util.DelayedTask(h);
6070         return function(e){
6071             // create new event object impl so new events don't wipe out properties
6072             e = new Roo.EventObjectImpl(e);
6073             task.delay(o.buffer, h, null, [e]);
6074         };
6075     };
6076
6077     var createSingle = function(h, el, ename, fn){
6078         return function(e){
6079             Roo.EventManager.removeListener(el, ename, fn);
6080             h(e);
6081         };
6082     };
6083
6084     var createDelayed = function(h, o){
6085         return function(e){
6086             // create new event object impl so new events don't wipe out properties
6087             e = new Roo.EventObjectImpl(e);
6088             setTimeout(function(){
6089                 h(e);
6090             }, o.delay || 10);
6091         };
6092     };
6093
6094     var listen = function(element, ename, opt, fn, scope){
6095         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6096         fn = fn || o.fn; scope = scope || o.scope;
6097         var el = Roo.getDom(element);
6098         if(!el){
6099             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6100         }
6101         var h = function(e){
6102             e = Roo.EventObject.setEvent(e);
6103             var t;
6104             if(o.delegate){
6105                 t = e.getTarget(o.delegate, el);
6106                 if(!t){
6107                     return;
6108                 }
6109             }else{
6110                 t = e.target;
6111             }
6112             if(o.stopEvent === true){
6113                 e.stopEvent();
6114             }
6115             if(o.preventDefault === true){
6116                e.preventDefault();
6117             }
6118             if(o.stopPropagation === true){
6119                 e.stopPropagation();
6120             }
6121
6122             if(o.normalized === false){
6123                 e = e.browserEvent;
6124             }
6125
6126             fn.call(scope || el, e, t, o);
6127         };
6128         if(o.delay){
6129             h = createDelayed(h, o);
6130         }
6131         if(o.single){
6132             h = createSingle(h, el, ename, fn);
6133         }
6134         if(o.buffer){
6135             h = createBuffered(h, o);
6136         }
6137         fn._handlers = fn._handlers || [];
6138         fn._handlers.push([Roo.id(el), ename, h]);
6139
6140         E.on(el, ename, h);
6141         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6142             el.addEventListener("DOMMouseScroll", h, false);
6143             E.on(window, 'unload', function(){
6144                 el.removeEventListener("DOMMouseScroll", h, false);
6145             });
6146         }
6147         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6148             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6149         }
6150         return h;
6151     };
6152
6153     var stopListening = function(el, ename, fn){
6154         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6155         if(hds){
6156             for(var i = 0, len = hds.length; i < len; i++){
6157                 var h = hds[i];
6158                 if(h[0] == id && h[1] == ename){
6159                     hd = h[2];
6160                     hds.splice(i, 1);
6161                     break;
6162                 }
6163             }
6164         }
6165         E.un(el, ename, hd);
6166         el = Roo.getDom(el);
6167         if(ename == "mousewheel" && el.addEventListener){
6168             el.removeEventListener("DOMMouseScroll", hd, false);
6169         }
6170         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6171             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6172         }
6173     };
6174
6175     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6176     
6177     var pub = {
6178         
6179         
6180         /** 
6181          * Fix for doc tools
6182          * @scope Roo.EventManager
6183          */
6184         
6185         
6186         /** 
6187          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6188          * object with a Roo.EventObject
6189          * @param {Function} fn        The method the event invokes
6190          * @param {Object}   scope    An object that becomes the scope of the handler
6191          * @param {boolean}  override If true, the obj passed in becomes
6192          *                             the execution scope of the listener
6193          * @return {Function} The wrapped function
6194          * @deprecated
6195          */
6196         wrap : function(fn, scope, override){
6197             return function(e){
6198                 Roo.EventObject.setEvent(e);
6199                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6200             };
6201         },
6202         
6203         /**
6204      * Appends an event handler to an element (shorthand for addListener)
6205      * @param {String/HTMLElement}   element        The html element or id to assign the
6206      * @param {String}   eventName The type of event to listen for
6207      * @param {Function} handler The method the event invokes
6208      * @param {Object}   scope (optional) The scope in which to execute the handler
6209      * function. The handler function's "this" context.
6210      * @param {Object}   options (optional) An object containing handler configuration
6211      * properties. This may contain any of the following properties:<ul>
6212      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6213      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6214      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6215      * <li>preventDefault {Boolean} True to prevent the default action</li>
6216      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6217      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6218      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6219      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6220      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6221      * by the specified number of milliseconds. If the event fires again within that time, the original
6222      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6223      * </ul><br>
6224      * <p>
6225      * <b>Combining Options</b><br>
6226      * Using the options argument, it is possible to combine different types of listeners:<br>
6227      * <br>
6228      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6229      * Code:<pre><code>
6230 el.on('click', this.onClick, this, {
6231     single: true,
6232     delay: 100,
6233     stopEvent : true,
6234     forumId: 4
6235 });</code></pre>
6236      * <p>
6237      * <b>Attaching multiple handlers in 1 call</b><br>
6238       * The method also allows for a single argument to be passed which is a config object containing properties
6239      * which specify multiple handlers.
6240      * <p>
6241      * Code:<pre><code>
6242 el.on({
6243     'click' : {
6244         fn: this.onClick
6245         scope: this,
6246         delay: 100
6247     },
6248     'mouseover' : {
6249         fn: this.onMouseOver
6250         scope: this
6251     },
6252     'mouseout' : {
6253         fn: this.onMouseOut
6254         scope: this
6255     }
6256 });</code></pre>
6257      * <p>
6258      * Or a shorthand syntax:<br>
6259      * Code:<pre><code>
6260 el.on({
6261     'click' : this.onClick,
6262     'mouseover' : this.onMouseOver,
6263     'mouseout' : this.onMouseOut
6264     scope: this
6265 });</code></pre>
6266      */
6267         addListener : function(element, eventName, fn, scope, options){
6268             if(typeof eventName == "object"){
6269                 var o = eventName;
6270                 for(var e in o){
6271                     if(propRe.test(e)){
6272                         continue;
6273                     }
6274                     if(typeof o[e] == "function"){
6275                         // shared options
6276                         listen(element, e, o, o[e], o.scope);
6277                     }else{
6278                         // individual options
6279                         listen(element, e, o[e]);
6280                     }
6281                 }
6282                 return;
6283             }
6284             return listen(element, eventName, options, fn, scope);
6285         },
6286         
6287         /**
6288          * Removes an event handler
6289          *
6290          * @param {String/HTMLElement}   element        The id or html element to remove the 
6291          *                             event from
6292          * @param {String}   eventName     The type of event
6293          * @param {Function} fn
6294          * @return {Boolean} True if a listener was actually removed
6295          */
6296         removeListener : function(element, eventName, fn){
6297             return stopListening(element, eventName, fn);
6298         },
6299         
6300         /**
6301          * Fires when the document is ready (before onload and before images are loaded). Can be 
6302          * accessed shorthanded Roo.onReady().
6303          * @param {Function} fn        The method the event invokes
6304          * @param {Object}   scope    An  object that becomes the scope of the handler
6305          * @param {boolean}  options
6306          */
6307         onDocumentReady : function(fn, scope, options){
6308             if(docReadyState){ // if it already fired
6309                 docReadyEvent.addListener(fn, scope, options);
6310                 docReadyEvent.fire();
6311                 docReadyEvent.clearListeners();
6312                 return;
6313             }
6314             if(!docReadyEvent){
6315                 initDocReady();
6316             }
6317             docReadyEvent.addListener(fn, scope, options);
6318         },
6319         
6320         /**
6321          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6322          * @param {Function} fn        The method the event invokes
6323          * @param {Object}   scope    An object that becomes the scope of the handler
6324          * @param {boolean}  options
6325          */
6326         onWindowResize : function(fn, scope, options){
6327             if(!resizeEvent){
6328                 resizeEvent = new Roo.util.Event();
6329                 resizeTask = new Roo.util.DelayedTask(function(){
6330                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6331                 });
6332                 E.on(window, "resize", function(){
6333                     if(Roo.isIE){
6334                         resizeTask.delay(50);
6335                     }else{
6336                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6337                     }
6338                 });
6339             }
6340             resizeEvent.addListener(fn, scope, options);
6341         },
6342
6343         /**
6344          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6345          * @param {Function} fn        The method the event invokes
6346          * @param {Object}   scope    An object that becomes the scope of the handler
6347          * @param {boolean}  options
6348          */
6349         onTextResize : function(fn, scope, options){
6350             if(!textEvent){
6351                 textEvent = new Roo.util.Event();
6352                 var textEl = new Roo.Element(document.createElement('div'));
6353                 textEl.dom.className = 'x-text-resize';
6354                 textEl.dom.innerHTML = 'X';
6355                 textEl.appendTo(document.body);
6356                 textSize = textEl.dom.offsetHeight;
6357                 setInterval(function(){
6358                     if(textEl.dom.offsetHeight != textSize){
6359                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6360                     }
6361                 }, this.textResizeInterval);
6362             }
6363             textEvent.addListener(fn, scope, options);
6364         },
6365
6366         /**
6367          * Removes the passed window resize listener.
6368          * @param {Function} fn        The method the event invokes
6369          * @param {Object}   scope    The scope of handler
6370          */
6371         removeResizeListener : function(fn, scope){
6372             if(resizeEvent){
6373                 resizeEvent.removeListener(fn, scope);
6374             }
6375         },
6376
6377         // private
6378         fireResize : function(){
6379             if(resizeEvent){
6380                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6381             }   
6382         },
6383         /**
6384          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6385          */
6386         ieDeferSrc : false,
6387         /**
6388          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6389          */
6390         textResizeInterval : 50
6391     };
6392     
6393     /**
6394      * Fix for doc tools
6395      * @scopeAlias pub=Roo.EventManager
6396      */
6397     
6398      /**
6399      * Appends an event handler to an element (shorthand for addListener)
6400      * @param {String/HTMLElement}   element        The html element or id to assign the
6401      * @param {String}   eventName The type of event to listen for
6402      * @param {Function} handler The method the event invokes
6403      * @param {Object}   scope (optional) The scope in which to execute the handler
6404      * function. The handler function's "this" context.
6405      * @param {Object}   options (optional) An object containing handler configuration
6406      * properties. This may contain any of the following properties:<ul>
6407      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6408      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6409      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6410      * <li>preventDefault {Boolean} True to prevent the default action</li>
6411      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6412      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6413      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6414      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6415      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6416      * by the specified number of milliseconds. If the event fires again within that time, the original
6417      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6418      * </ul><br>
6419      * <p>
6420      * <b>Combining Options</b><br>
6421      * Using the options argument, it is possible to combine different types of listeners:<br>
6422      * <br>
6423      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6424      * Code:<pre><code>
6425 el.on('click', this.onClick, this, {
6426     single: true,
6427     delay: 100,
6428     stopEvent : true,
6429     forumId: 4
6430 });</code></pre>
6431      * <p>
6432      * <b>Attaching multiple handlers in 1 call</b><br>
6433       * The method also allows for a single argument to be passed which is a config object containing properties
6434      * which specify multiple handlers.
6435      * <p>
6436      * Code:<pre><code>
6437 el.on({
6438     'click' : {
6439         fn: this.onClick
6440         scope: this,
6441         delay: 100
6442     },
6443     'mouseover' : {
6444         fn: this.onMouseOver
6445         scope: this
6446     },
6447     'mouseout' : {
6448         fn: this.onMouseOut
6449         scope: this
6450     }
6451 });</code></pre>
6452      * <p>
6453      * Or a shorthand syntax:<br>
6454      * Code:<pre><code>
6455 el.on({
6456     'click' : this.onClick,
6457     'mouseover' : this.onMouseOver,
6458     'mouseout' : this.onMouseOut
6459     scope: this
6460 });</code></pre>
6461      */
6462     pub.on = pub.addListener;
6463     pub.un = pub.removeListener;
6464
6465     pub.stoppedMouseDownEvent = new Roo.util.Event();
6466     return pub;
6467 }();
6468 /**
6469   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6470   * @param {Function} fn        The method the event invokes
6471   * @param {Object}   scope    An  object that becomes the scope of the handler
6472   * @param {boolean}  override If true, the obj passed in becomes
6473   *                             the execution scope of the listener
6474   * @member Roo
6475   * @method onReady
6476  */
6477 Roo.onReady = Roo.EventManager.onDocumentReady;
6478
6479 Roo.onReady(function(){
6480     var bd = Roo.get(document.body);
6481     if(!bd){ return; }
6482
6483     var cls = [
6484             Roo.isIE ? "roo-ie"
6485             : Roo.isGecko ? "roo-gecko"
6486             : Roo.isOpera ? "roo-opera"
6487             : Roo.isSafari ? "roo-safari" : ""];
6488
6489     if(Roo.isMac){
6490         cls.push("roo-mac");
6491     }
6492     if(Roo.isLinux){
6493         cls.push("roo-linux");
6494     }
6495     if(Roo.isBorderBox){
6496         cls.push('roo-border-box');
6497     }
6498     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6499         var p = bd.dom.parentNode;
6500         if(p){
6501             p.className += ' roo-strict';
6502         }
6503     }
6504     bd.addClass(cls.join(' '));
6505 });
6506
6507 /**
6508  * @class Roo.EventObject
6509  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6510  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6511  * Example:
6512  * <pre><code>
6513  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6514     e.preventDefault();
6515     var target = e.getTarget();
6516     ...
6517  }
6518  var myDiv = Roo.get("myDiv");
6519  myDiv.on("click", handleClick);
6520  //or
6521  Roo.EventManager.on("myDiv", 'click', handleClick);
6522  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6523  </code></pre>
6524  * @singleton
6525  */
6526 Roo.EventObject = function(){
6527     
6528     var E = Roo.lib.Event;
6529     
6530     // safari keypress events for special keys return bad keycodes
6531     var safariKeys = {
6532         63234 : 37, // left
6533         63235 : 39, // right
6534         63232 : 38, // up
6535         63233 : 40, // down
6536         63276 : 33, // page up
6537         63277 : 34, // page down
6538         63272 : 46, // delete
6539         63273 : 36, // home
6540         63275 : 35  // end
6541     };
6542
6543     // normalize button clicks
6544     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6545                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6546
6547     Roo.EventObjectImpl = function(e){
6548         if(e){
6549             this.setEvent(e.browserEvent || e);
6550         }
6551     };
6552     Roo.EventObjectImpl.prototype = {
6553         /**
6554          * Used to fix doc tools.
6555          * @scope Roo.EventObject.prototype
6556          */
6557             
6558
6559         
6560         
6561         /** The normal browser event */
6562         browserEvent : null,
6563         /** The button pressed in a mouse event */
6564         button : -1,
6565         /** True if the shift key was down during the event */
6566         shiftKey : false,
6567         /** True if the control key was down during the event */
6568         ctrlKey : false,
6569         /** True if the alt key was down during the event */
6570         altKey : false,
6571
6572         /** Key constant 
6573         * @type Number */
6574         BACKSPACE : 8,
6575         /** Key constant 
6576         * @type Number */
6577         TAB : 9,
6578         /** Key constant 
6579         * @type Number */
6580         RETURN : 13,
6581         /** Key constant 
6582         * @type Number */
6583         ENTER : 13,
6584         /** Key constant 
6585         * @type Number */
6586         SHIFT : 16,
6587         /** Key constant 
6588         * @type Number */
6589         CONTROL : 17,
6590         /** Key constant 
6591         * @type Number */
6592         ESC : 27,
6593         /** Key constant 
6594         * @type Number */
6595         SPACE : 32,
6596         /** Key constant 
6597         * @type Number */
6598         PAGEUP : 33,
6599         /** Key constant 
6600         * @type Number */
6601         PAGEDOWN : 34,
6602         /** Key constant 
6603         * @type Number */
6604         END : 35,
6605         /** Key constant 
6606         * @type Number */
6607         HOME : 36,
6608         /** Key constant 
6609         * @type Number */
6610         LEFT : 37,
6611         /** Key constant 
6612         * @type Number */
6613         UP : 38,
6614         /** Key constant 
6615         * @type Number */
6616         RIGHT : 39,
6617         /** Key constant 
6618         * @type Number */
6619         DOWN : 40,
6620         /** Key constant 
6621         * @type Number */
6622         DELETE : 46,
6623         /** Key constant 
6624         * @type Number */
6625         F5 : 116,
6626
6627            /** @private */
6628         setEvent : function(e){
6629             if(e == this || (e && e.browserEvent)){ // already wrapped
6630                 return e;
6631             }
6632             this.browserEvent = e;
6633             if(e){
6634                 // normalize buttons
6635                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6636                 if(e.type == 'click' && this.button == -1){
6637                     this.button = 0;
6638                 }
6639                 this.type = e.type;
6640                 this.shiftKey = e.shiftKey;
6641                 // mac metaKey behaves like ctrlKey
6642                 this.ctrlKey = e.ctrlKey || e.metaKey;
6643                 this.altKey = e.altKey;
6644                 // in getKey these will be normalized for the mac
6645                 this.keyCode = e.keyCode;
6646                 // keyup warnings on firefox.
6647                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6648                 // cache the target for the delayed and or buffered events
6649                 this.target = E.getTarget(e);
6650                 // same for XY
6651                 this.xy = E.getXY(e);
6652             }else{
6653                 this.button = -1;
6654                 this.shiftKey = false;
6655                 this.ctrlKey = false;
6656                 this.altKey = false;
6657                 this.keyCode = 0;
6658                 this.charCode =0;
6659                 this.target = null;
6660                 this.xy = [0, 0];
6661             }
6662             return this;
6663         },
6664
6665         /**
6666          * Stop the event (preventDefault and stopPropagation)
6667          */
6668         stopEvent : function(){
6669             if(this.browserEvent){
6670                 if(this.browserEvent.type == 'mousedown'){
6671                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6672                 }
6673                 E.stopEvent(this.browserEvent);
6674             }
6675         },
6676
6677         /**
6678          * Prevents the browsers default handling of the event.
6679          */
6680         preventDefault : function(){
6681             if(this.browserEvent){
6682                 E.preventDefault(this.browserEvent);
6683             }
6684         },
6685
6686         /** @private */
6687         isNavKeyPress : function(){
6688             var k = this.keyCode;
6689             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6690             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6691         },
6692
6693         isSpecialKey : function(){
6694             var k = this.keyCode;
6695             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6696             (k == 16) || (k == 17) ||
6697             (k >= 18 && k <= 20) ||
6698             (k >= 33 && k <= 35) ||
6699             (k >= 36 && k <= 39) ||
6700             (k >= 44 && k <= 45);
6701         },
6702         /**
6703          * Cancels bubbling of the event.
6704          */
6705         stopPropagation : function(){
6706             if(this.browserEvent){
6707                 if(this.type == 'mousedown'){
6708                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6709                 }
6710                 E.stopPropagation(this.browserEvent);
6711             }
6712         },
6713
6714         /**
6715          * Gets the key code for the event.
6716          * @return {Number}
6717          */
6718         getCharCode : function(){
6719             return this.charCode || this.keyCode;
6720         },
6721
6722         /**
6723          * Returns a normalized keyCode for the event.
6724          * @return {Number} The key code
6725          */
6726         getKey : function(){
6727             var k = this.keyCode || this.charCode;
6728             return Roo.isSafari ? (safariKeys[k] || k) : k;
6729         },
6730
6731         /**
6732          * Gets the x coordinate of the event.
6733          * @return {Number}
6734          */
6735         getPageX : function(){
6736             return this.xy[0];
6737         },
6738
6739         /**
6740          * Gets the y coordinate of the event.
6741          * @return {Number}
6742          */
6743         getPageY : function(){
6744             return this.xy[1];
6745         },
6746
6747         /**
6748          * Gets the time of the event.
6749          * @return {Number}
6750          */
6751         getTime : function(){
6752             if(this.browserEvent){
6753                 return E.getTime(this.browserEvent);
6754             }
6755             return null;
6756         },
6757
6758         /**
6759          * Gets the page coordinates of the event.
6760          * @return {Array} The xy values like [x, y]
6761          */
6762         getXY : function(){
6763             return this.xy;
6764         },
6765
6766         /**
6767          * Gets the target for the event.
6768          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6769          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6770                 search as a number or element (defaults to 10 || document.body)
6771          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6772          * @return {HTMLelement}
6773          */
6774         getTarget : function(selector, maxDepth, returnEl){
6775             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6776         },
6777         /**
6778          * Gets the related target.
6779          * @return {HTMLElement}
6780          */
6781         getRelatedTarget : function(){
6782             if(this.browserEvent){
6783                 return E.getRelatedTarget(this.browserEvent);
6784             }
6785             return null;
6786         },
6787
6788         /**
6789          * Normalizes mouse wheel delta across browsers
6790          * @return {Number} The delta
6791          */
6792         getWheelDelta : function(){
6793             var e = this.browserEvent;
6794             var delta = 0;
6795             if(e.wheelDelta){ /* IE/Opera. */
6796                 delta = e.wheelDelta/120;
6797             }else if(e.detail){ /* Mozilla case. */
6798                 delta = -e.detail/3;
6799             }
6800             return delta;
6801         },
6802
6803         /**
6804          * Returns true if the control, meta, shift or alt key was pressed during this event.
6805          * @return {Boolean}
6806          */
6807         hasModifier : function(){
6808             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6809         },
6810
6811         /**
6812          * Returns true if the target of this event equals el or is a child of el
6813          * @param {String/HTMLElement/Element} el
6814          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6815          * @return {Boolean}
6816          */
6817         within : function(el, related){
6818             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6819             return t && Roo.fly(el).contains(t);
6820         },
6821
6822         getPoint : function(){
6823             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6824         }
6825     };
6826
6827     return new Roo.EventObjectImpl();
6828 }();
6829             
6830     /*
6831  * Based on:
6832  * Ext JS Library 1.1.1
6833  * Copyright(c) 2006-2007, Ext JS, LLC.
6834  *
6835  * Originally Released Under LGPL - original licence link has changed is not relivant.
6836  *
6837  * Fork - LGPL
6838  * <script type="text/javascript">
6839  */
6840
6841  
6842 // was in Composite Element!??!?!
6843  
6844 (function(){
6845     var D = Roo.lib.Dom;
6846     var E = Roo.lib.Event;
6847     var A = Roo.lib.Anim;
6848
6849     // local style camelizing for speed
6850     var propCache = {};
6851     var camelRe = /(-[a-z])/gi;
6852     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6853     var view = document.defaultView;
6854
6855 /**
6856  * @class Roo.Element
6857  * Represents an Element in the DOM.<br><br>
6858  * Usage:<br>
6859 <pre><code>
6860 var el = Roo.get("my-div");
6861
6862 // or with getEl
6863 var el = getEl("my-div");
6864
6865 // or with a DOM element
6866 var el = Roo.get(myDivElement);
6867 </code></pre>
6868  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6869  * each call instead of constructing a new one.<br><br>
6870  * <b>Animations</b><br />
6871  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6872  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6873 <pre>
6874 Option    Default   Description
6875 --------- --------  ---------------------------------------------
6876 duration  .35       The duration of the animation in seconds
6877 easing    easeOut   The YUI easing method
6878 callback  none      A function to execute when the anim completes
6879 scope     this      The scope (this) of the callback function
6880 </pre>
6881 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6882 * manipulate the animation. Here's an example:
6883 <pre><code>
6884 var el = Roo.get("my-div");
6885
6886 // no animation
6887 el.setWidth(100);
6888
6889 // default animation
6890 el.setWidth(100, true);
6891
6892 // animation with some options set
6893 el.setWidth(100, {
6894     duration: 1,
6895     callback: this.foo,
6896     scope: this
6897 });
6898
6899 // using the "anim" property to get the Anim object
6900 var opt = {
6901     duration: 1,
6902     callback: this.foo,
6903     scope: this
6904 };
6905 el.setWidth(100, opt);
6906 ...
6907 if(opt.anim.isAnimated()){
6908     opt.anim.stop();
6909 }
6910 </code></pre>
6911 * <b> Composite (Collections of) Elements</b><br />
6912  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6913  * @constructor Create a new Element directly.
6914  * @param {String/HTMLElement} element
6915  * @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).
6916  */
6917     Roo.Element = function(element, forceNew){
6918         var dom = typeof element == "string" ?
6919                 document.getElementById(element) : element;
6920         if(!dom){ // invalid id/element
6921             return null;
6922         }
6923         var id = dom.id;
6924         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6925             return Roo.Element.cache[id];
6926         }
6927
6928         /**
6929          * The DOM element
6930          * @type HTMLElement
6931          */
6932         this.dom = dom;
6933
6934         /**
6935          * The DOM element ID
6936          * @type String
6937          */
6938         this.id = id || Roo.id(dom);
6939     };
6940
6941     var El = Roo.Element;
6942
6943     El.prototype = {
6944         /**
6945          * The element's default display mode  (defaults to "")
6946          * @type String
6947          */
6948         originalDisplay : "",
6949
6950         visibilityMode : 1,
6951         /**
6952          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6953          * @type String
6954          */
6955         defaultUnit : "px",
6956         /**
6957          * Sets the element's visibility mode. When setVisible() is called it
6958          * will use this to determine whether to set the visibility or the display property.
6959          * @param visMode Element.VISIBILITY or Element.DISPLAY
6960          * @return {Roo.Element} this
6961          */
6962         setVisibilityMode : function(visMode){
6963             this.visibilityMode = visMode;
6964             return this;
6965         },
6966         /**
6967          * Convenience method for setVisibilityMode(Element.DISPLAY)
6968          * @param {String} display (optional) What to set display to when visible
6969          * @return {Roo.Element} this
6970          */
6971         enableDisplayMode : function(display){
6972             this.setVisibilityMode(El.DISPLAY);
6973             if(typeof display != "undefined") this.originalDisplay = display;
6974             return this;
6975         },
6976
6977         /**
6978          * 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)
6979          * @param {String} selector The simple selector to test
6980          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6981                 search as a number or element (defaults to 10 || document.body)
6982          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6983          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6984          */
6985         findParent : function(simpleSelector, maxDepth, returnEl){
6986             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6987             maxDepth = maxDepth || 50;
6988             if(typeof maxDepth != "number"){
6989                 stopEl = Roo.getDom(maxDepth);
6990                 maxDepth = 10;
6991             }
6992             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6993                 if(dq.is(p, simpleSelector)){
6994                     return returnEl ? Roo.get(p) : p;
6995                 }
6996                 depth++;
6997                 p = p.parentNode;
6998             }
6999             return null;
7000         },
7001
7002
7003         /**
7004          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7005          * @param {String} selector The simple selector to test
7006          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7007                 search as a number or element (defaults to 10 || document.body)
7008          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7009          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7010          */
7011         findParentNode : function(simpleSelector, maxDepth, returnEl){
7012             var p = Roo.fly(this.dom.parentNode, '_internal');
7013             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7014         },
7015
7016         /**
7017          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7018          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7019          * @param {String} selector The simple selector to test
7020          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7021                 search as a number or element (defaults to 10 || document.body)
7022          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7023          */
7024         up : function(simpleSelector, maxDepth){
7025             return this.findParentNode(simpleSelector, maxDepth, true);
7026         },
7027
7028
7029
7030         /**
7031          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7032          * @param {String} selector The simple selector to test
7033          * @return {Boolean} True if this element matches the selector, else false
7034          */
7035         is : function(simpleSelector){
7036             return Roo.DomQuery.is(this.dom, simpleSelector);
7037         },
7038
7039         /**
7040          * Perform animation on this element.
7041          * @param {Object} args The YUI animation control args
7042          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7043          * @param {Function} onComplete (optional) Function to call when animation completes
7044          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7045          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7046          * @return {Roo.Element} this
7047          */
7048         animate : function(args, duration, onComplete, easing, animType){
7049             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7050             return this;
7051         },
7052
7053         /*
7054          * @private Internal animation call
7055          */
7056         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7057             animType = animType || 'run';
7058             opt = opt || {};
7059             var anim = Roo.lib.Anim[animType](
7060                 this.dom, args,
7061                 (opt.duration || defaultDur) || .35,
7062                 (opt.easing || defaultEase) || 'easeOut',
7063                 function(){
7064                     Roo.callback(cb, this);
7065                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7066                 },
7067                 this
7068             );
7069             opt.anim = anim;
7070             return anim;
7071         },
7072
7073         // private legacy anim prep
7074         preanim : function(a, i){
7075             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7076         },
7077
7078         /**
7079          * Removes worthless text nodes
7080          * @param {Boolean} forceReclean (optional) By default the element
7081          * keeps track if it has been cleaned already so
7082          * you can call this over and over. However, if you update the element and
7083          * need to force a reclean, you can pass true.
7084          */
7085         clean : function(forceReclean){
7086             if(this.isCleaned && forceReclean !== true){
7087                 return this;
7088             }
7089             var ns = /\S/;
7090             var d = this.dom, n = d.firstChild, ni = -1;
7091             while(n){
7092                 var nx = n.nextSibling;
7093                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7094                     d.removeChild(n);
7095                 }else{
7096                     n.nodeIndex = ++ni;
7097                 }
7098                 n = nx;
7099             }
7100             this.isCleaned = true;
7101             return this;
7102         },
7103
7104         // private
7105         calcOffsetsTo : function(el){
7106             el = Roo.get(el);
7107             var d = el.dom;
7108             var restorePos = false;
7109             if(el.getStyle('position') == 'static'){
7110                 el.position('relative');
7111                 restorePos = true;
7112             }
7113             var x = 0, y =0;
7114             var op = this.dom;
7115             while(op && op != d && op.tagName != 'HTML'){
7116                 x+= op.offsetLeft;
7117                 y+= op.offsetTop;
7118                 op = op.offsetParent;
7119             }
7120             if(restorePos){
7121                 el.position('static');
7122             }
7123             return [x, y];
7124         },
7125
7126         /**
7127          * Scrolls this element into view within the passed container.
7128          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7129          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7130          * @return {Roo.Element} this
7131          */
7132         scrollIntoView : function(container, hscroll){
7133             var c = Roo.getDom(container) || document.body;
7134             var el = this.dom;
7135
7136             var o = this.calcOffsetsTo(c),
7137                 l = o[0],
7138                 t = o[1],
7139                 b = t+el.offsetHeight,
7140                 r = l+el.offsetWidth;
7141
7142             var ch = c.clientHeight;
7143             var ct = parseInt(c.scrollTop, 10);
7144             var cl = parseInt(c.scrollLeft, 10);
7145             var cb = ct + ch;
7146             var cr = cl + c.clientWidth;
7147
7148             if(t < ct){
7149                 c.scrollTop = t;
7150             }else if(b > cb){
7151                 c.scrollTop = b-ch;
7152             }
7153
7154             if(hscroll !== false){
7155                 if(l < cl){
7156                     c.scrollLeft = l;
7157                 }else if(r > cr){
7158                     c.scrollLeft = r-c.clientWidth;
7159                 }
7160             }
7161             return this;
7162         },
7163
7164         // private
7165         scrollChildIntoView : function(child, hscroll){
7166             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7167         },
7168
7169         /**
7170          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7171          * the new height may not be available immediately.
7172          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7173          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7174          * @param {Function} onComplete (optional) Function to call when animation completes
7175          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7176          * @return {Roo.Element} this
7177          */
7178         autoHeight : function(animate, duration, onComplete, easing){
7179             var oldHeight = this.getHeight();
7180             this.clip();
7181             this.setHeight(1); // force clipping
7182             setTimeout(function(){
7183                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7184                 if(!animate){
7185                     this.setHeight(height);
7186                     this.unclip();
7187                     if(typeof onComplete == "function"){
7188                         onComplete();
7189                     }
7190                 }else{
7191                     this.setHeight(oldHeight); // restore original height
7192                     this.setHeight(height, animate, duration, function(){
7193                         this.unclip();
7194                         if(typeof onComplete == "function") onComplete();
7195                     }.createDelegate(this), easing);
7196                 }
7197             }.createDelegate(this), 0);
7198             return this;
7199         },
7200
7201         /**
7202          * Returns true if this element is an ancestor of the passed element
7203          * @param {HTMLElement/String} el The element to check
7204          * @return {Boolean} True if this element is an ancestor of el, else false
7205          */
7206         contains : function(el){
7207             if(!el){return false;}
7208             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7209         },
7210
7211         /**
7212          * Checks whether the element is currently visible using both visibility and display properties.
7213          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7214          * @return {Boolean} True if the element is currently visible, else false
7215          */
7216         isVisible : function(deep) {
7217             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7218             if(deep !== true || !vis){
7219                 return vis;
7220             }
7221             var p = this.dom.parentNode;
7222             while(p && p.tagName.toLowerCase() != "body"){
7223                 if(!Roo.fly(p, '_isVisible').isVisible()){
7224                     return false;
7225                 }
7226                 p = p.parentNode;
7227             }
7228             return true;
7229         },
7230
7231         /**
7232          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7233          * @param {String} selector The CSS selector
7234          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7235          * @return {CompositeElement/CompositeElementLite} The composite element
7236          */
7237         select : function(selector, unique){
7238             return El.select(selector, unique, this.dom);
7239         },
7240
7241         /**
7242          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7243          * @param {String} selector The CSS selector
7244          * @return {Array} An array of the matched nodes
7245          */
7246         query : function(selector, unique){
7247             return Roo.DomQuery.select(selector, this.dom);
7248         },
7249
7250         /**
7251          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7252          * @param {String} selector The CSS selector
7253          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7254          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7255          */
7256         child : function(selector, returnDom){
7257             var n = Roo.DomQuery.selectNode(selector, this.dom);
7258             return returnDom ? n : Roo.get(n);
7259         },
7260
7261         /**
7262          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7263          * @param {String} selector The CSS selector
7264          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7265          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7266          */
7267         down : function(selector, returnDom){
7268             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7269             return returnDom ? n : Roo.get(n);
7270         },
7271
7272         /**
7273          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7274          * @param {String} group The group the DD object is member of
7275          * @param {Object} config The DD config object
7276          * @param {Object} overrides An object containing methods to override/implement on the DD object
7277          * @return {Roo.dd.DD} The DD object
7278          */
7279         initDD : function(group, config, overrides){
7280             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7281             return Roo.apply(dd, overrides);
7282         },
7283
7284         /**
7285          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7286          * @param {String} group The group the DDProxy object is member of
7287          * @param {Object} config The DDProxy config object
7288          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7289          * @return {Roo.dd.DDProxy} The DDProxy object
7290          */
7291         initDDProxy : function(group, config, overrides){
7292             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7293             return Roo.apply(dd, overrides);
7294         },
7295
7296         /**
7297          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7298          * @param {String} group The group the DDTarget object is member of
7299          * @param {Object} config The DDTarget config object
7300          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7301          * @return {Roo.dd.DDTarget} The DDTarget object
7302          */
7303         initDDTarget : function(group, config, overrides){
7304             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7305             return Roo.apply(dd, overrides);
7306         },
7307
7308         /**
7309          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7310          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7311          * @param {Boolean} visible Whether the element is visible
7312          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7313          * @return {Roo.Element} this
7314          */
7315          setVisible : function(visible, animate){
7316             if(!animate || !A){
7317                 if(this.visibilityMode == El.DISPLAY){
7318                     this.setDisplayed(visible);
7319                 }else{
7320                     this.fixDisplay();
7321                     this.dom.style.visibility = visible ? "visible" : "hidden";
7322                 }
7323             }else{
7324                 // closure for composites
7325                 var dom = this.dom;
7326                 var visMode = this.visibilityMode;
7327                 if(visible){
7328                     this.setOpacity(.01);
7329                     this.setVisible(true);
7330                 }
7331                 this.anim({opacity: { to: (visible?1:0) }},
7332                       this.preanim(arguments, 1),
7333                       null, .35, 'easeIn', function(){
7334                          if(!visible){
7335                              if(visMode == El.DISPLAY){
7336                                  dom.style.display = "none";
7337                              }else{
7338                                  dom.style.visibility = "hidden";
7339                              }
7340                              Roo.get(dom).setOpacity(1);
7341                          }
7342                      });
7343             }
7344             return this;
7345         },
7346
7347         /**
7348          * Returns true if display is not "none"
7349          * @return {Boolean}
7350          */
7351         isDisplayed : function() {
7352             return this.getStyle("display") != "none";
7353         },
7354
7355         /**
7356          * Toggles the element's visibility or display, depending on visibility mode.
7357          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7358          * @return {Roo.Element} this
7359          */
7360         toggle : function(animate){
7361             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7362             return this;
7363         },
7364
7365         /**
7366          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7367          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7368          * @return {Roo.Element} this
7369          */
7370         setDisplayed : function(value) {
7371             if(typeof value == "boolean"){
7372                value = value ? this.originalDisplay : "none";
7373             }
7374             this.setStyle("display", value);
7375             return this;
7376         },
7377
7378         /**
7379          * Tries to focus the element. Any exceptions are caught and ignored.
7380          * @return {Roo.Element} this
7381          */
7382         focus : function() {
7383             try{
7384                 this.dom.focus();
7385             }catch(e){}
7386             return this;
7387         },
7388
7389         /**
7390          * Tries to blur the element. Any exceptions are caught and ignored.
7391          * @return {Roo.Element} this
7392          */
7393         blur : function() {
7394             try{
7395                 this.dom.blur();
7396             }catch(e){}
7397             return this;
7398         },
7399
7400         /**
7401          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7402          * @param {String/Array} className The CSS class to add, or an array of classes
7403          * @return {Roo.Element} this
7404          */
7405         addClass : function(className){
7406             if(className instanceof Array){
7407                 for(var i = 0, len = className.length; i < len; i++) {
7408                     this.addClass(className[i]);
7409                 }
7410             }else{
7411                 if(className && !this.hasClass(className)){
7412                     this.dom.className = this.dom.className + " " + className;
7413                 }
7414             }
7415             return this;
7416         },
7417
7418         /**
7419          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7420          * @param {String/Array} className The CSS class to add, or an array of classes
7421          * @return {Roo.Element} this
7422          */
7423         radioClass : function(className){
7424             var siblings = this.dom.parentNode.childNodes;
7425             for(var i = 0; i < siblings.length; i++) {
7426                 var s = siblings[i];
7427                 if(s.nodeType == 1){
7428                     Roo.get(s).removeClass(className);
7429                 }
7430             }
7431             this.addClass(className);
7432             return this;
7433         },
7434
7435         /**
7436          * Removes one or more CSS classes from the element.
7437          * @param {String/Array} className The CSS class to remove, or an array of classes
7438          * @return {Roo.Element} this
7439          */
7440         removeClass : function(className){
7441             if(!className || !this.dom.className){
7442                 return this;
7443             }
7444             if(className instanceof Array){
7445                 for(var i = 0, len = className.length; i < len; i++) {
7446                     this.removeClass(className[i]);
7447                 }
7448             }else{
7449                 if(this.hasClass(className)){
7450                     var re = this.classReCache[className];
7451                     if (!re) {
7452                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7453                        this.classReCache[className] = re;
7454                     }
7455                     this.dom.className =
7456                         this.dom.className.replace(re, " ");
7457                 }
7458             }
7459             return this;
7460         },
7461
7462         // private
7463         classReCache: {},
7464
7465         /**
7466          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7467          * @param {String} className The CSS class to toggle
7468          * @return {Roo.Element} this
7469          */
7470         toggleClass : function(className){
7471             if(this.hasClass(className)){
7472                 this.removeClass(className);
7473             }else{
7474                 this.addClass(className);
7475             }
7476             return this;
7477         },
7478
7479         /**
7480          * Checks if the specified CSS class exists on this element's DOM node.
7481          * @param {String} className The CSS class to check for
7482          * @return {Boolean} True if the class exists, else false
7483          */
7484         hasClass : function(className){
7485             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7486         },
7487
7488         /**
7489          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7490          * @param {String} oldClassName The CSS class to replace
7491          * @param {String} newClassName The replacement CSS class
7492          * @return {Roo.Element} this
7493          */
7494         replaceClass : function(oldClassName, newClassName){
7495             this.removeClass(oldClassName);
7496             this.addClass(newClassName);
7497             return this;
7498         },
7499
7500         /**
7501          * Returns an object with properties matching the styles requested.
7502          * For example, el.getStyles('color', 'font-size', 'width') might return
7503          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7504          * @param {String} style1 A style name
7505          * @param {String} style2 A style name
7506          * @param {String} etc.
7507          * @return {Object} The style object
7508          */
7509         getStyles : function(){
7510             var a = arguments, len = a.length, r = {};
7511             for(var i = 0; i < len; i++){
7512                 r[a[i]] = this.getStyle(a[i]);
7513             }
7514             return r;
7515         },
7516
7517         /**
7518          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7519          * @param {String} property The style property whose value is returned.
7520          * @return {String} The current value of the style property for this element.
7521          */
7522         getStyle : function(){
7523             return view && view.getComputedStyle ?
7524                 function(prop){
7525                     var el = this.dom, v, cs, camel;
7526                     if(prop == 'float'){
7527                         prop = "cssFloat";
7528                     }
7529                     if(el.style && (v = el.style[prop])){
7530                         return v;
7531                     }
7532                     if(cs = view.getComputedStyle(el, "")){
7533                         if(!(camel = propCache[prop])){
7534                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7535                         }
7536                         return cs[camel];
7537                     }
7538                     return null;
7539                 } :
7540                 function(prop){
7541                     var el = this.dom, v, cs, camel;
7542                     if(prop == 'opacity'){
7543                         if(typeof el.style.filter == 'string'){
7544                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7545                             if(m){
7546                                 var fv = parseFloat(m[1]);
7547                                 if(!isNaN(fv)){
7548                                     return fv ? fv / 100 : 0;
7549                                 }
7550                             }
7551                         }
7552                         return 1;
7553                     }else if(prop == 'float'){
7554                         prop = "styleFloat";
7555                     }
7556                     if(!(camel = propCache[prop])){
7557                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7558                     }
7559                     if(v = el.style[camel]){
7560                         return v;
7561                     }
7562                     if(cs = el.currentStyle){
7563                         return cs[camel];
7564                     }
7565                     return null;
7566                 };
7567         }(),
7568
7569         /**
7570          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7571          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7572          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7573          * @return {Roo.Element} this
7574          */
7575         setStyle : function(prop, value){
7576             if(typeof prop == "string"){
7577                 
7578                 if (prop == 'float') {
7579                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7580                     return this;
7581                 }
7582                 
7583                 var camel;
7584                 if(!(camel = propCache[prop])){
7585                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                 }
7587                 
7588                 if(camel == 'opacity') {
7589                     this.setOpacity(value);
7590                 }else{
7591                     this.dom.style[camel] = value;
7592                 }
7593             }else{
7594                 for(var style in prop){
7595                     if(typeof prop[style] != "function"){
7596                        this.setStyle(style, prop[style]);
7597                     }
7598                 }
7599             }
7600             return this;
7601         },
7602
7603         /**
7604          * More flexible version of {@link #setStyle} for setting style properties.
7605          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7606          * a function which returns such a specification.
7607          * @return {Roo.Element} this
7608          */
7609         applyStyles : function(style){
7610             Roo.DomHelper.applyStyles(this.dom, style);
7611             return this;
7612         },
7613
7614         /**
7615           * 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).
7616           * @return {Number} The X position of the element
7617           */
7618         getX : function(){
7619             return D.getX(this.dom);
7620         },
7621
7622         /**
7623           * 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).
7624           * @return {Number} The Y position of the element
7625           */
7626         getY : function(){
7627             return D.getY(this.dom);
7628         },
7629
7630         /**
7631           * 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).
7632           * @return {Array} The XY position of the element
7633           */
7634         getXY : function(){
7635             return D.getXY(this.dom);
7636         },
7637
7638         /**
7639          * 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).
7640          * @param {Number} The X position of the element
7641          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7642          * @return {Roo.Element} this
7643          */
7644         setX : function(x, animate){
7645             if(!animate || !A){
7646                 D.setX(this.dom, x);
7647             }else{
7648                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7649             }
7650             return this;
7651         },
7652
7653         /**
7654          * 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).
7655          * @param {Number} The Y position of the element
7656          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7657          * @return {Roo.Element} this
7658          */
7659         setY : function(y, animate){
7660             if(!animate || !A){
7661                 D.setY(this.dom, y);
7662             }else{
7663                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7664             }
7665             return this;
7666         },
7667
7668         /**
7669          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7670          * @param {String} left The left CSS property value
7671          * @return {Roo.Element} this
7672          */
7673         setLeft : function(left){
7674             this.setStyle("left", this.addUnits(left));
7675             return this;
7676         },
7677
7678         /**
7679          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7680          * @param {String} top The top CSS property value
7681          * @return {Roo.Element} this
7682          */
7683         setTop : function(top){
7684             this.setStyle("top", this.addUnits(top));
7685             return this;
7686         },
7687
7688         /**
7689          * Sets the element's CSS right style.
7690          * @param {String} right The right CSS property value
7691          * @return {Roo.Element} this
7692          */
7693         setRight : function(right){
7694             this.setStyle("right", this.addUnits(right));
7695             return this;
7696         },
7697
7698         /**
7699          * Sets the element's CSS bottom style.
7700          * @param {String} bottom The bottom CSS property value
7701          * @return {Roo.Element} this
7702          */
7703         setBottom : function(bottom){
7704             this.setStyle("bottom", this.addUnits(bottom));
7705             return this;
7706         },
7707
7708         /**
7709          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7710          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7711          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7712          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7713          * @return {Roo.Element} this
7714          */
7715         setXY : function(pos, animate){
7716             if(!animate || !A){
7717                 D.setXY(this.dom, pos);
7718             }else{
7719                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7720             }
7721             return this;
7722         },
7723
7724         /**
7725          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7726          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7727          * @param {Number} x X value for new position (coordinates are page-based)
7728          * @param {Number} y Y value for new position (coordinates are page-based)
7729          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7730          * @return {Roo.Element} this
7731          */
7732         setLocation : function(x, y, animate){
7733             this.setXY([x, y], this.preanim(arguments, 2));
7734             return this;
7735         },
7736
7737         /**
7738          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7739          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7740          * @param {Number} x X value for new position (coordinates are page-based)
7741          * @param {Number} y Y value for new position (coordinates are page-based)
7742          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7743          * @return {Roo.Element} this
7744          */
7745         moveTo : function(x, y, animate){
7746             this.setXY([x, y], this.preanim(arguments, 2));
7747             return this;
7748         },
7749
7750         /**
7751          * Returns the region of the given element.
7752          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7753          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7754          */
7755         getRegion : function(){
7756             return D.getRegion(this.dom);
7757         },
7758
7759         /**
7760          * Returns the offset height of the element
7761          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7762          * @return {Number} The element's height
7763          */
7764         getHeight : function(contentHeight){
7765             var h = this.dom.offsetHeight || 0;
7766             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7767         },
7768
7769         /**
7770          * Returns the offset width of the element
7771          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7772          * @return {Number} The element's width
7773          */
7774         getWidth : function(contentWidth){
7775             var w = this.dom.offsetWidth || 0;
7776             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7777         },
7778
7779         /**
7780          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7781          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7782          * if a height has not been set using CSS.
7783          * @return {Number}
7784          */
7785         getComputedHeight : function(){
7786             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7787             if(!h){
7788                 h = parseInt(this.getStyle('height'), 10) || 0;
7789                 if(!this.isBorderBox()){
7790                     h += this.getFrameWidth('tb');
7791                 }
7792             }
7793             return h;
7794         },
7795
7796         /**
7797          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7798          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7799          * if a width has not been set using CSS.
7800          * @return {Number}
7801          */
7802         getComputedWidth : function(){
7803             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7804             if(!w){
7805                 w = parseInt(this.getStyle('width'), 10) || 0;
7806                 if(!this.isBorderBox()){
7807                     w += this.getFrameWidth('lr');
7808                 }
7809             }
7810             return w;
7811         },
7812
7813         /**
7814          * Returns the size of the element.
7815          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7816          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7817          */
7818         getSize : function(contentSize){
7819             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7820         },
7821
7822         /**
7823          * Returns the width and height of the viewport.
7824          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7825          */
7826         getViewSize : function(){
7827             var d = this.dom, doc = document, aw = 0, ah = 0;
7828             if(d == doc || d == doc.body){
7829                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7830             }else{
7831                 return {
7832                     width : d.clientWidth,
7833                     height: d.clientHeight
7834                 };
7835             }
7836         },
7837
7838         /**
7839          * Returns the value of the "value" attribute
7840          * @param {Boolean} asNumber true to parse the value as a number
7841          * @return {String/Number}
7842          */
7843         getValue : function(asNumber){
7844             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7845         },
7846
7847         // private
7848         adjustWidth : function(width){
7849             if(typeof width == "number"){
7850                 if(this.autoBoxAdjust && !this.isBorderBox()){
7851                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7852                 }
7853                 if(width < 0){
7854                     width = 0;
7855                 }
7856             }
7857             return width;
7858         },
7859
7860         // private
7861         adjustHeight : function(height){
7862             if(typeof height == "number"){
7863                if(this.autoBoxAdjust && !this.isBorderBox()){
7864                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7865                }
7866                if(height < 0){
7867                    height = 0;
7868                }
7869             }
7870             return height;
7871         },
7872
7873         /**
7874          * Set the width of the element
7875          * @param {Number} width The new width
7876          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7877          * @return {Roo.Element} this
7878          */
7879         setWidth : function(width, animate){
7880             width = this.adjustWidth(width);
7881             if(!animate || !A){
7882                 this.dom.style.width = this.addUnits(width);
7883             }else{
7884                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7885             }
7886             return this;
7887         },
7888
7889         /**
7890          * Set the height of the element
7891          * @param {Number} height The new height
7892          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7893          * @return {Roo.Element} this
7894          */
7895          setHeight : function(height, animate){
7896             height = this.adjustHeight(height);
7897             if(!animate || !A){
7898                 this.dom.style.height = this.addUnits(height);
7899             }else{
7900                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7901             }
7902             return this;
7903         },
7904
7905         /**
7906          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7907          * @param {Number} width The new width
7908          * @param {Number} height The new height
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          setSize : function(width, height, animate){
7913             if(typeof width == "object"){ // in case of object from getSize()
7914                 height = width.height; width = width.width;
7915             }
7916             width = this.adjustWidth(width); height = this.adjustHeight(height);
7917             if(!animate || !A){
7918                 this.dom.style.width = this.addUnits(width);
7919                 this.dom.style.height = this.addUnits(height);
7920             }else{
7921                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7922             }
7923             return this;
7924         },
7925
7926         /**
7927          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7928          * @param {Number} x X value for new position (coordinates are page-based)
7929          * @param {Number} y Y value for new position (coordinates are page-based)
7930          * @param {Number} width The new width
7931          * @param {Number} height The new height
7932          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7933          * @return {Roo.Element} this
7934          */
7935         setBounds : function(x, y, width, height, animate){
7936             if(!animate || !A){
7937                 this.setSize(width, height);
7938                 this.setLocation(x, y);
7939             }else{
7940                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7941                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7942                               this.preanim(arguments, 4), 'motion');
7943             }
7944             return this;
7945         },
7946
7947         /**
7948          * 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.
7949          * @param {Roo.lib.Region} region The region to fill
7950          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7951          * @return {Roo.Element} this
7952          */
7953         setRegion : function(region, animate){
7954             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7955             return this;
7956         },
7957
7958         /**
7959          * Appends an event handler
7960          *
7961          * @param {String}   eventName     The type of event to append
7962          * @param {Function} fn        The method the event invokes
7963          * @param {Object} scope       (optional) The scope (this object) of the fn
7964          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7965          */
7966         addListener : function(eventName, fn, scope, options){
7967             if (this.dom) {
7968                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7969             }
7970         },
7971
7972         /**
7973          * Removes an event handler from this element
7974          * @param {String} eventName the type of event to remove
7975          * @param {Function} fn the method the event invokes
7976          * @return {Roo.Element} this
7977          */
7978         removeListener : function(eventName, fn){
7979             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7980             return this;
7981         },
7982
7983         /**
7984          * Removes all previous added listeners from this element
7985          * @return {Roo.Element} this
7986          */
7987         removeAllListeners : function(){
7988             E.purgeElement(this.dom);
7989             return this;
7990         },
7991
7992         relayEvent : function(eventName, observable){
7993             this.on(eventName, function(e){
7994                 observable.fireEvent(eventName, e);
7995             });
7996         },
7997
7998         /**
7999          * Set the opacity of the element
8000          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8001          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8002          * @return {Roo.Element} this
8003          */
8004          setOpacity : function(opacity, animate){
8005             if(!animate || !A){
8006                 var s = this.dom.style;
8007                 if(Roo.isIE){
8008                     s.zoom = 1;
8009                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8010                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8011                 }else{
8012                     s.opacity = opacity;
8013                 }
8014             }else{
8015                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8016             }
8017             return this;
8018         },
8019
8020         /**
8021          * Gets the left X coordinate
8022          * @param {Boolean} local True to get the local css position instead of page coordinate
8023          * @return {Number}
8024          */
8025         getLeft : function(local){
8026             if(!local){
8027                 return this.getX();
8028             }else{
8029                 return parseInt(this.getStyle("left"), 10) || 0;
8030             }
8031         },
8032
8033         /**
8034          * Gets the right X coordinate of the element (element X position + element width)
8035          * @param {Boolean} local True to get the local css position instead of page coordinate
8036          * @return {Number}
8037          */
8038         getRight : function(local){
8039             if(!local){
8040                 return this.getX() + this.getWidth();
8041             }else{
8042                 return (this.getLeft(true) + this.getWidth()) || 0;
8043             }
8044         },
8045
8046         /**
8047          * Gets the top Y coordinate
8048          * @param {Boolean} local True to get the local css position instead of page coordinate
8049          * @return {Number}
8050          */
8051         getTop : function(local) {
8052             if(!local){
8053                 return this.getY();
8054             }else{
8055                 return parseInt(this.getStyle("top"), 10) || 0;
8056             }
8057         },
8058
8059         /**
8060          * Gets the bottom Y coordinate of the element (element Y position + element height)
8061          * @param {Boolean} local True to get the local css position instead of page coordinate
8062          * @return {Number}
8063          */
8064         getBottom : function(local){
8065             if(!local){
8066                 return this.getY() + this.getHeight();
8067             }else{
8068                 return (this.getTop(true) + this.getHeight()) || 0;
8069             }
8070         },
8071
8072         /**
8073         * Initializes positioning on this element. If a desired position is not passed, it will make the
8074         * the element positioned relative IF it is not already positioned.
8075         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8076         * @param {Number} zIndex (optional) The zIndex to apply
8077         * @param {Number} x (optional) Set the page X position
8078         * @param {Number} y (optional) Set the page Y position
8079         */
8080         position : function(pos, zIndex, x, y){
8081             if(!pos){
8082                if(this.getStyle('position') == 'static'){
8083                    this.setStyle('position', 'relative');
8084                }
8085             }else{
8086                 this.setStyle("position", pos);
8087             }
8088             if(zIndex){
8089                 this.setStyle("z-index", zIndex);
8090             }
8091             if(x !== undefined && y !== undefined){
8092                 this.setXY([x, y]);
8093             }else if(x !== undefined){
8094                 this.setX(x);
8095             }else if(y !== undefined){
8096                 this.setY(y);
8097             }
8098         },
8099
8100         /**
8101         * Clear positioning back to the default when the document was loaded
8102         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8103         * @return {Roo.Element} this
8104          */
8105         clearPositioning : function(value){
8106             value = value ||'';
8107             this.setStyle({
8108                 "left": value,
8109                 "right": value,
8110                 "top": value,
8111                 "bottom": value,
8112                 "z-index": "",
8113                 "position" : "static"
8114             });
8115             return this;
8116         },
8117
8118         /**
8119         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8120         * snapshot before performing an update and then restoring the element.
8121         * @return {Object}
8122         */
8123         getPositioning : function(){
8124             var l = this.getStyle("left");
8125             var t = this.getStyle("top");
8126             return {
8127                 "position" : this.getStyle("position"),
8128                 "left" : l,
8129                 "right" : l ? "" : this.getStyle("right"),
8130                 "top" : t,
8131                 "bottom" : t ? "" : this.getStyle("bottom"),
8132                 "z-index" : this.getStyle("z-index")
8133             };
8134         },
8135
8136         /**
8137          * Gets the width of the border(s) for the specified side(s)
8138          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8139          * passing lr would get the border (l)eft width + the border (r)ight width.
8140          * @return {Number} The width of the sides passed added together
8141          */
8142         getBorderWidth : function(side){
8143             return this.addStyles(side, El.borders);
8144         },
8145
8146         /**
8147          * Gets the width of the padding(s) for the specified side(s)
8148          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8149          * passing lr would get the padding (l)eft + the padding (r)ight.
8150          * @return {Number} The padding of the sides passed added together
8151          */
8152         getPadding : function(side){
8153             return this.addStyles(side, El.paddings);
8154         },
8155
8156         /**
8157         * Set positioning with an object returned by getPositioning().
8158         * @param {Object} posCfg
8159         * @return {Roo.Element} this
8160          */
8161         setPositioning : function(pc){
8162             this.applyStyles(pc);
8163             if(pc.right == "auto"){
8164                 this.dom.style.right = "";
8165             }
8166             if(pc.bottom == "auto"){
8167                 this.dom.style.bottom = "";
8168             }
8169             return this;
8170         },
8171
8172         // private
8173         fixDisplay : function(){
8174             if(this.getStyle("display") == "none"){
8175                 this.setStyle("visibility", "hidden");
8176                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8177                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8178                     this.setStyle("display", "block");
8179                 }
8180             }
8181         },
8182
8183         /**
8184          * Quick set left and top adding default units
8185          * @param {String} left The left CSS property value
8186          * @param {String} top The top CSS property value
8187          * @return {Roo.Element} this
8188          */
8189          setLeftTop : function(left, top){
8190             this.dom.style.left = this.addUnits(left);
8191             this.dom.style.top = this.addUnits(top);
8192             return this;
8193         },
8194
8195         /**
8196          * Move this element relative to its current position.
8197          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8198          * @param {Number} distance How far to move the element in pixels
8199          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8200          * @return {Roo.Element} this
8201          */
8202          move : function(direction, distance, animate){
8203             var xy = this.getXY();
8204             direction = direction.toLowerCase();
8205             switch(direction){
8206                 case "l":
8207                 case "left":
8208                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8209                     break;
8210                case "r":
8211                case "right":
8212                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8213                     break;
8214                case "t":
8215                case "top":
8216                case "up":
8217                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8218                     break;
8219                case "b":
8220                case "bottom":
8221                case "down":
8222                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8223                     break;
8224             }
8225             return this;
8226         },
8227
8228         /**
8229          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8230          * @return {Roo.Element} this
8231          */
8232         clip : function(){
8233             if(!this.isClipped){
8234                this.isClipped = true;
8235                this.originalClip = {
8236                    "o": this.getStyle("overflow"),
8237                    "x": this.getStyle("overflow-x"),
8238                    "y": this.getStyle("overflow-y")
8239                };
8240                this.setStyle("overflow", "hidden");
8241                this.setStyle("overflow-x", "hidden");
8242                this.setStyle("overflow-y", "hidden");
8243             }
8244             return this;
8245         },
8246
8247         /**
8248          *  Return clipping (overflow) to original clipping before clip() was called
8249          * @return {Roo.Element} this
8250          */
8251         unclip : function(){
8252             if(this.isClipped){
8253                 this.isClipped = false;
8254                 var o = this.originalClip;
8255                 if(o.o){this.setStyle("overflow", o.o);}
8256                 if(o.x){this.setStyle("overflow-x", o.x);}
8257                 if(o.y){this.setStyle("overflow-y", o.y);}
8258             }
8259             return this;
8260         },
8261
8262
8263         /**
8264          * Gets the x,y coordinates specified by the anchor position on the element.
8265          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8266          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8267          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8268          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8269          * @return {Array} [x, y] An array containing the element's x and y coordinates
8270          */
8271         getAnchorXY : function(anchor, local, s){
8272             //Passing a different size is useful for pre-calculating anchors,
8273             //especially for anchored animations that change the el size.
8274
8275             var w, h, vp = false;
8276             if(!s){
8277                 var d = this.dom;
8278                 if(d == document.body || d == document){
8279                     vp = true;
8280                     w = D.getViewWidth(); h = D.getViewHeight();
8281                 }else{
8282                     w = this.getWidth(); h = this.getHeight();
8283                 }
8284             }else{
8285                 w = s.width;  h = s.height;
8286             }
8287             var x = 0, y = 0, r = Math.round;
8288             switch((anchor || "tl").toLowerCase()){
8289                 case "c":
8290                     x = r(w*.5);
8291                     y = r(h*.5);
8292                 break;
8293                 case "t":
8294                     x = r(w*.5);
8295                     y = 0;
8296                 break;
8297                 case "l":
8298                     x = 0;
8299                     y = r(h*.5);
8300                 break;
8301                 case "r":
8302                     x = w;
8303                     y = r(h*.5);
8304                 break;
8305                 case "b":
8306                     x = r(w*.5);
8307                     y = h;
8308                 break;
8309                 case "tl":
8310                     x = 0;
8311                     y = 0;
8312                 break;
8313                 case "bl":
8314                     x = 0;
8315                     y = h;
8316                 break;
8317                 case "br":
8318                     x = w;
8319                     y = h;
8320                 break;
8321                 case "tr":
8322                     x = w;
8323                     y = 0;
8324                 break;
8325             }
8326             if(local === true){
8327                 return [x, y];
8328             }
8329             if(vp){
8330                 var sc = this.getScroll();
8331                 return [x + sc.left, y + sc.top];
8332             }
8333             //Add the element's offset xy
8334             var o = this.getXY();
8335             return [x+o[0], y+o[1]];
8336         },
8337
8338         /**
8339          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8340          * supported position values.
8341          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8342          * @param {String} position The position to align to.
8343          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8344          * @return {Array} [x, y]
8345          */
8346         getAlignToXY : function(el, p, o){
8347             el = Roo.get(el);
8348             var d = this.dom;
8349             if(!el.dom){
8350                 throw "Element.alignTo with an element that doesn't exist";
8351             }
8352             var c = false; //constrain to viewport
8353             var p1 = "", p2 = "";
8354             o = o || [0,0];
8355
8356             if(!p){
8357                 p = "tl-bl";
8358             }else if(p == "?"){
8359                 p = "tl-bl?";
8360             }else if(p.indexOf("-") == -1){
8361                 p = "tl-" + p;
8362             }
8363             p = p.toLowerCase();
8364             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8365             if(!m){
8366                throw "Element.alignTo with an invalid alignment " + p;
8367             }
8368             p1 = m[1]; p2 = m[2]; c = !!m[3];
8369
8370             //Subtract the aligned el's internal xy from the target's offset xy
8371             //plus custom offset to get the aligned el's new offset xy
8372             var a1 = this.getAnchorXY(p1, true);
8373             var a2 = el.getAnchorXY(p2, false);
8374             var x = a2[0] - a1[0] + o[0];
8375             var y = a2[1] - a1[1] + o[1];
8376             if(c){
8377                 //constrain the aligned el to viewport if necessary
8378                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8379                 // 5px of margin for ie
8380                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8381
8382                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8383                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8384                 //otherwise swap the aligned el to the opposite border of the target.
8385                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8386                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8387                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8388                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8389
8390                var doc = document;
8391                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8392                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8393
8394                if((x+w) > dw + scrollX){
8395                     x = swapX ? r.left-w : dw+scrollX-w;
8396                 }
8397                if(x < scrollX){
8398                    x = swapX ? r.right : scrollX;
8399                }
8400                if((y+h) > dh + scrollY){
8401                     y = swapY ? r.top-h : dh+scrollY-h;
8402                 }
8403                if (y < scrollY){
8404                    y = swapY ? r.bottom : scrollY;
8405                }
8406             }
8407             return [x,y];
8408         },
8409
8410         // private
8411         getConstrainToXY : function(){
8412             var os = {top:0, left:0, bottom:0, right: 0};
8413
8414             return function(el, local, offsets, proposedXY){
8415                 el = Roo.get(el);
8416                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8417
8418                 var vw, vh, vx = 0, vy = 0;
8419                 if(el.dom == document.body || el.dom == document){
8420                     vw = Roo.lib.Dom.getViewWidth();
8421                     vh = Roo.lib.Dom.getViewHeight();
8422                 }else{
8423                     vw = el.dom.clientWidth;
8424                     vh = el.dom.clientHeight;
8425                     if(!local){
8426                         var vxy = el.getXY();
8427                         vx = vxy[0];
8428                         vy = vxy[1];
8429                     }
8430                 }
8431
8432                 var s = el.getScroll();
8433
8434                 vx += offsets.left + s.left;
8435                 vy += offsets.top + s.top;
8436
8437                 vw -= offsets.right;
8438                 vh -= offsets.bottom;
8439
8440                 var vr = vx+vw;
8441                 var vb = vy+vh;
8442
8443                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8444                 var x = xy[0], y = xy[1];
8445                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8446
8447                 // only move it if it needs it
8448                 var moved = false;
8449
8450                 // first validate right/bottom
8451                 if((x + w) > vr){
8452                     x = vr - w;
8453                     moved = true;
8454                 }
8455                 if((y + h) > vb){
8456                     y = vb - h;
8457                     moved = true;
8458                 }
8459                 // then make sure top/left isn't negative
8460                 if(x < vx){
8461                     x = vx;
8462                     moved = true;
8463                 }
8464                 if(y < vy){
8465                     y = vy;
8466                     moved = true;
8467                 }
8468                 return moved ? [x, y] : false;
8469             };
8470         }(),
8471
8472         // private
8473         adjustForConstraints : function(xy, parent, offsets){
8474             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8475         },
8476
8477         /**
8478          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8479          * document it aligns it to the viewport.
8480          * The position parameter is optional, and can be specified in any one of the following formats:
8481          * <ul>
8482          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8483          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8484          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8485          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8486          *   <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
8487          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8488          * </ul>
8489          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8490          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8491          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8492          * that specified in order to enforce the viewport constraints.
8493          * Following are all of the supported anchor positions:
8494     <pre>
8495     Value  Description
8496     -----  -----------------------------
8497     tl     The top left corner (default)
8498     t      The center of the top edge
8499     tr     The top right corner
8500     l      The center of the left edge
8501     c      In the center of the element
8502     r      The center of the right edge
8503     bl     The bottom left corner
8504     b      The center of the bottom edge
8505     br     The bottom right corner
8506     </pre>
8507     Example Usage:
8508     <pre><code>
8509     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8510     el.alignTo("other-el");
8511
8512     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8513     el.alignTo("other-el", "tr?");
8514
8515     // align the bottom right corner of el with the center left edge of other-el
8516     el.alignTo("other-el", "br-l?");
8517
8518     // align the center of el with the bottom left corner of other-el and
8519     // adjust the x position by -6 pixels (and the y position by 0)
8520     el.alignTo("other-el", "c-bl", [-6, 0]);
8521     </code></pre>
8522          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8523          * @param {String} position The position to align to.
8524          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8525          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8526          * @return {Roo.Element} this
8527          */
8528         alignTo : function(element, position, offsets, animate){
8529             var xy = this.getAlignToXY(element, position, offsets);
8530             this.setXY(xy, this.preanim(arguments, 3));
8531             return this;
8532         },
8533
8534         /**
8535          * Anchors an element to another element and realigns it when the window is resized.
8536          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8537          * @param {String} position The position to align to.
8538          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8539          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8540          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8541          * is a number, it is used as the buffer delay (defaults to 50ms).
8542          * @param {Function} callback The function to call after the animation finishes
8543          * @return {Roo.Element} this
8544          */
8545         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8546             var action = function(){
8547                 this.alignTo(el, alignment, offsets, animate);
8548                 Roo.callback(callback, this);
8549             };
8550             Roo.EventManager.onWindowResize(action, this);
8551             var tm = typeof monitorScroll;
8552             if(tm != 'undefined'){
8553                 Roo.EventManager.on(window, 'scroll', action, this,
8554                     {buffer: tm == 'number' ? monitorScroll : 50});
8555             }
8556             action.call(this); // align immediately
8557             return this;
8558         },
8559         /**
8560          * Clears any opacity settings from this element. Required in some cases for IE.
8561          * @return {Roo.Element} this
8562          */
8563         clearOpacity : function(){
8564             if (window.ActiveXObject) {
8565                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8566                     this.dom.style.filter = "";
8567                 }
8568             } else {
8569                 this.dom.style.opacity = "";
8570                 this.dom.style["-moz-opacity"] = "";
8571                 this.dom.style["-khtml-opacity"] = "";
8572             }
8573             return this;
8574         },
8575
8576         /**
8577          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8578          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8579          * @return {Roo.Element} this
8580          */
8581         hide : function(animate){
8582             this.setVisible(false, this.preanim(arguments, 0));
8583             return this;
8584         },
8585
8586         /**
8587         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8588         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8589          * @return {Roo.Element} this
8590          */
8591         show : function(animate){
8592             this.setVisible(true, this.preanim(arguments, 0));
8593             return this;
8594         },
8595
8596         /**
8597          * @private Test if size has a unit, otherwise appends the default
8598          */
8599         addUnits : function(size){
8600             return Roo.Element.addUnits(size, this.defaultUnit);
8601         },
8602
8603         /**
8604          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8605          * @return {Roo.Element} this
8606          */
8607         beginMeasure : function(){
8608             var el = this.dom;
8609             if(el.offsetWidth || el.offsetHeight){
8610                 return this; // offsets work already
8611             }
8612             var changed = [];
8613             var p = this.dom, b = document.body; // start with this element
8614             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8615                 var pe = Roo.get(p);
8616                 if(pe.getStyle('display') == 'none'){
8617                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8618                     p.style.visibility = "hidden";
8619                     p.style.display = "block";
8620                 }
8621                 p = p.parentNode;
8622             }
8623             this._measureChanged = changed;
8624             return this;
8625
8626         },
8627
8628         /**
8629          * Restores displays to before beginMeasure was called
8630          * @return {Roo.Element} this
8631          */
8632         endMeasure : function(){
8633             var changed = this._measureChanged;
8634             if(changed){
8635                 for(var i = 0, len = changed.length; i < len; i++) {
8636                     var r = changed[i];
8637                     r.el.style.visibility = r.visibility;
8638                     r.el.style.display = "none";
8639                 }
8640                 this._measureChanged = null;
8641             }
8642             return this;
8643         },
8644
8645         /**
8646         * Update the innerHTML of this element, optionally searching for and processing scripts
8647         * @param {String} html The new HTML
8648         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8649         * @param {Function} callback For async script loading you can be noticed when the update completes
8650         * @return {Roo.Element} this
8651          */
8652         update : function(html, loadScripts, callback){
8653             if(typeof html == "undefined"){
8654                 html = "";
8655             }
8656             if(loadScripts !== true){
8657                 this.dom.innerHTML = html;
8658                 if(typeof callback == "function"){
8659                     callback();
8660                 }
8661                 return this;
8662             }
8663             var id = Roo.id();
8664             var dom = this.dom;
8665
8666             html += '<span id="' + id + '"></span>';
8667
8668             E.onAvailable(id, function(){
8669                 var hd = document.getElementsByTagName("head")[0];
8670                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8671                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8672                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8673
8674                 var match;
8675                 while(match = re.exec(html)){
8676                     var attrs = match[1];
8677                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8678                     if(srcMatch && srcMatch[2]){
8679                        var s = document.createElement("script");
8680                        s.src = srcMatch[2];
8681                        var typeMatch = attrs.match(typeRe);
8682                        if(typeMatch && typeMatch[2]){
8683                            s.type = typeMatch[2];
8684                        }
8685                        hd.appendChild(s);
8686                     }else if(match[2] && match[2].length > 0){
8687                         if(window.execScript) {
8688                            window.execScript(match[2]);
8689                         } else {
8690                             /**
8691                              * eval:var:id
8692                              * eval:var:dom
8693                              * eval:var:html
8694                              * 
8695                              */
8696                            window.eval(match[2]);
8697                         }
8698                     }
8699                 }
8700                 var el = document.getElementById(id);
8701                 if(el){el.parentNode.removeChild(el);}
8702                 if(typeof callback == "function"){
8703                     callback();
8704                 }
8705             });
8706             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8707             return this;
8708         },
8709
8710         /**
8711          * Direct access to the UpdateManager update() method (takes the same parameters).
8712          * @param {String/Function} url The url for this request or a function to call to get the url
8713          * @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}
8714          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8715          * @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.
8716          * @return {Roo.Element} this
8717          */
8718         load : function(){
8719             var um = this.getUpdateManager();
8720             um.update.apply(um, arguments);
8721             return this;
8722         },
8723
8724         /**
8725         * Gets this element's UpdateManager
8726         * @return {Roo.UpdateManager} The UpdateManager
8727         */
8728         getUpdateManager : function(){
8729             if(!this.updateManager){
8730                 this.updateManager = new Roo.UpdateManager(this);
8731             }
8732             return this.updateManager;
8733         },
8734
8735         /**
8736          * Disables text selection for this element (normalized across browsers)
8737          * @return {Roo.Element} this
8738          */
8739         unselectable : function(){
8740             this.dom.unselectable = "on";
8741             this.swallowEvent("selectstart", true);
8742             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8743             this.addClass("x-unselectable");
8744             return this;
8745         },
8746
8747         /**
8748         * Calculates the x, y to center this element on the screen
8749         * @return {Array} The x, y values [x, y]
8750         */
8751         getCenterXY : function(){
8752             return this.getAlignToXY(document, 'c-c');
8753         },
8754
8755         /**
8756         * Centers the Element in either the viewport, or another Element.
8757         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8758         */
8759         center : function(centerIn){
8760             this.alignTo(centerIn || document, 'c-c');
8761             return this;
8762         },
8763
8764         /**
8765          * Tests various css rules/browsers to determine if this element uses a border box
8766          * @return {Boolean}
8767          */
8768         isBorderBox : function(){
8769             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8770         },
8771
8772         /**
8773          * Return a box {x, y, width, height} that can be used to set another elements
8774          * size/location to match this element.
8775          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8776          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8777          * @return {Object} box An object in the format {x, y, width, height}
8778          */
8779         getBox : function(contentBox, local){
8780             var xy;
8781             if(!local){
8782                 xy = this.getXY();
8783             }else{
8784                 var left = parseInt(this.getStyle("left"), 10) || 0;
8785                 var top = parseInt(this.getStyle("top"), 10) || 0;
8786                 xy = [left, top];
8787             }
8788             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8789             if(!contentBox){
8790                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8791             }else{
8792                 var l = this.getBorderWidth("l")+this.getPadding("l");
8793                 var r = this.getBorderWidth("r")+this.getPadding("r");
8794                 var t = this.getBorderWidth("t")+this.getPadding("t");
8795                 var b = this.getBorderWidth("b")+this.getPadding("b");
8796                 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)};
8797             }
8798             bx.right = bx.x + bx.width;
8799             bx.bottom = bx.y + bx.height;
8800             return bx;
8801         },
8802
8803         /**
8804          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8805          for more information about the sides.
8806          * @param {String} sides
8807          * @return {Number}
8808          */
8809         getFrameWidth : function(sides, onlyContentBox){
8810             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8811         },
8812
8813         /**
8814          * 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.
8815          * @param {Object} box The box to fill {x, y, width, height}
8816          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8817          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8818          * @return {Roo.Element} this
8819          */
8820         setBox : function(box, adjust, animate){
8821             var w = box.width, h = box.height;
8822             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8823                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8824                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8825             }
8826             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8827             return this;
8828         },
8829
8830         /**
8831          * Forces the browser to repaint this element
8832          * @return {Roo.Element} this
8833          */
8834          repaint : function(){
8835             var dom = this.dom;
8836             this.addClass("x-repaint");
8837             setTimeout(function(){
8838                 Roo.get(dom).removeClass("x-repaint");
8839             }, 1);
8840             return this;
8841         },
8842
8843         /**
8844          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8845          * then it returns the calculated width of the sides (see getPadding)
8846          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8847          * @return {Object/Number}
8848          */
8849         getMargins : function(side){
8850             if(!side){
8851                 return {
8852                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8853                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8854                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8855                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8856                 };
8857             }else{
8858                 return this.addStyles(side, El.margins);
8859              }
8860         },
8861
8862         // private
8863         addStyles : function(sides, styles){
8864             var val = 0, v, w;
8865             for(var i = 0, len = sides.length; i < len; i++){
8866                 v = this.getStyle(styles[sides.charAt(i)]);
8867                 if(v){
8868                      w = parseInt(v, 10);
8869                      if(w){ val += w; }
8870                 }
8871             }
8872             return val;
8873         },
8874
8875         /**
8876          * Creates a proxy element of this element
8877          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8878          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8879          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8880          * @return {Roo.Element} The new proxy element
8881          */
8882         createProxy : function(config, renderTo, matchBox){
8883             if(renderTo){
8884                 renderTo = Roo.getDom(renderTo);
8885             }else{
8886                 renderTo = document.body;
8887             }
8888             config = typeof config == "object" ?
8889                 config : {tag : "div", cls: config};
8890             var proxy = Roo.DomHelper.append(renderTo, config, true);
8891             if(matchBox){
8892                proxy.setBox(this.getBox());
8893             }
8894             return proxy;
8895         },
8896
8897         /**
8898          * Puts a mask over this element to disable user interaction. Requires core.css.
8899          * This method can only be applied to elements which accept child nodes.
8900          * @param {String} msg (optional) A message to display in the mask
8901          * @param {String} msgCls (optional) A css class to apply to the msg element
8902          * @return {Element} The mask  element
8903          */
8904         mask : function(msg, msgCls)
8905         {
8906             if(this.getStyle("position") == "static"){
8907                 this.setStyle("position", "relative");
8908             }
8909             if(!this._mask){
8910                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8911             }
8912             this.addClass("x-masked");
8913             this._mask.setDisplayed(true);
8914             
8915             // we wander
8916             var z = 0;
8917             var dom = this.dom
8918             while (dom && dom.style) {
8919                 if (!isNaN(parseInt(dom.style.zIndex))) {
8920                     z = Math.max(z, parseInt(dom.style.zIndex));
8921                 }
8922                 dom = dom.parentNode;
8923             }
8924             // if we are masking the body - then it hides everything..
8925             if (this.dom == document.body) {
8926                 z = 1000000;
8927                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8928                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8929             }
8930            
8931             if(typeof msg == 'string'){
8932                 if(!this._maskMsg){
8933                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8934                 }
8935                 var mm = this._maskMsg;
8936                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8937                 mm.dom.firstChild.innerHTML = msg;
8938                 mm.setDisplayed(true);
8939                 mm.center(this);
8940                 mm.setStyle('z-index', z + 102);
8941             }
8942             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8943                 this._mask.setHeight(this.getHeight());
8944             }
8945             this._mask.setStyle('z-index', z + 100);
8946             
8947             return this._mask;
8948         },
8949
8950         /**
8951          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8952          * it is cached for reuse.
8953          */
8954         unmask : function(removeEl){
8955             if(this._mask){
8956                 if(removeEl === true){
8957                     this._mask.remove();
8958                     delete this._mask;
8959                     if(this._maskMsg){
8960                         this._maskMsg.remove();
8961                         delete this._maskMsg;
8962                     }
8963                 }else{
8964                     this._mask.setDisplayed(false);
8965                     if(this._maskMsg){
8966                         this._maskMsg.setDisplayed(false);
8967                     }
8968                 }
8969             }
8970             this.removeClass("x-masked");
8971         },
8972
8973         /**
8974          * Returns true if this element is masked
8975          * @return {Boolean}
8976          */
8977         isMasked : function(){
8978             return this._mask && this._mask.isVisible();
8979         },
8980
8981         /**
8982          * Creates an iframe shim for this element to keep selects and other windowed objects from
8983          * showing through.
8984          * @return {Roo.Element} The new shim element
8985          */
8986         createShim : function(){
8987             var el = document.createElement('iframe');
8988             el.frameBorder = 'no';
8989             el.className = 'roo-shim';
8990             if(Roo.isIE && Roo.isSecure){
8991                 el.src = Roo.SSL_SECURE_URL;
8992             }
8993             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8994             shim.autoBoxAdjust = false;
8995             return shim;
8996         },
8997
8998         /**
8999          * Removes this element from the DOM and deletes it from the cache
9000          */
9001         remove : function(){
9002             if(this.dom.parentNode){
9003                 this.dom.parentNode.removeChild(this.dom);
9004             }
9005             delete El.cache[this.dom.id];
9006         },
9007
9008         /**
9009          * Sets up event handlers to add and remove a css class when the mouse is over this element
9010          * @param {String} className
9011          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9012          * mouseout events for children elements
9013          * @return {Roo.Element} this
9014          */
9015         addClassOnOver : function(className, preventFlicker){
9016             this.on("mouseover", function(){
9017                 Roo.fly(this, '_internal').addClass(className);
9018             }, this.dom);
9019             var removeFn = function(e){
9020                 if(preventFlicker !== true || !e.within(this, true)){
9021                     Roo.fly(this, '_internal').removeClass(className);
9022                 }
9023             };
9024             this.on("mouseout", removeFn, this.dom);
9025             return this;
9026         },
9027
9028         /**
9029          * Sets up event handlers to add and remove a css class when this element has the focus
9030          * @param {String} className
9031          * @return {Roo.Element} this
9032          */
9033         addClassOnFocus : function(className){
9034             this.on("focus", function(){
9035                 Roo.fly(this, '_internal').addClass(className);
9036             }, this.dom);
9037             this.on("blur", function(){
9038                 Roo.fly(this, '_internal').removeClass(className);
9039             }, this.dom);
9040             return this;
9041         },
9042         /**
9043          * 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)
9044          * @param {String} className
9045          * @return {Roo.Element} this
9046          */
9047         addClassOnClick : function(className){
9048             var dom = this.dom;
9049             this.on("mousedown", function(){
9050                 Roo.fly(dom, '_internal').addClass(className);
9051                 var d = Roo.get(document);
9052                 var fn = function(){
9053                     Roo.fly(dom, '_internal').removeClass(className);
9054                     d.removeListener("mouseup", fn);
9055                 };
9056                 d.on("mouseup", fn);
9057             });
9058             return this;
9059         },
9060
9061         /**
9062          * Stops the specified event from bubbling and optionally prevents the default action
9063          * @param {String} eventName
9064          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9065          * @return {Roo.Element} this
9066          */
9067         swallowEvent : function(eventName, preventDefault){
9068             var fn = function(e){
9069                 e.stopPropagation();
9070                 if(preventDefault){
9071                     e.preventDefault();
9072                 }
9073             };
9074             if(eventName instanceof Array){
9075                 for(var i = 0, len = eventName.length; i < len; i++){
9076                      this.on(eventName[i], fn);
9077                 }
9078                 return this;
9079             }
9080             this.on(eventName, fn);
9081             return this;
9082         },
9083
9084         /**
9085          * @private
9086          */
9087       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9088
9089         /**
9090          * Sizes this element to its parent element's dimensions performing
9091          * neccessary box adjustments.
9092          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9093          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9094          * @return {Roo.Element} this
9095          */
9096         fitToParent : function(monitorResize, targetParent) {
9097           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9098           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9099           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9100             return;
9101           }
9102           var p = Roo.get(targetParent || this.dom.parentNode);
9103           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9104           if (monitorResize === true) {
9105             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9106             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9107           }
9108           return this;
9109         },
9110
9111         /**
9112          * Gets the next sibling, skipping text nodes
9113          * @return {HTMLElement} The next sibling or null
9114          */
9115         getNextSibling : function(){
9116             var n = this.dom.nextSibling;
9117             while(n && n.nodeType != 1){
9118                 n = n.nextSibling;
9119             }
9120             return n;
9121         },
9122
9123         /**
9124          * Gets the previous sibling, skipping text nodes
9125          * @return {HTMLElement} The previous sibling or null
9126          */
9127         getPrevSibling : function(){
9128             var n = this.dom.previousSibling;
9129             while(n && n.nodeType != 1){
9130                 n = n.previousSibling;
9131             }
9132             return n;
9133         },
9134
9135
9136         /**
9137          * Appends the passed element(s) to this element
9138          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9139          * @return {Roo.Element} this
9140          */
9141         appendChild: function(el){
9142             el = Roo.get(el);
9143             el.appendTo(this);
9144             return this;
9145         },
9146
9147         /**
9148          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9149          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9150          * automatically generated with the specified attributes.
9151          * @param {HTMLElement} insertBefore (optional) a child element of this element
9152          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9153          * @return {Roo.Element} The new child element
9154          */
9155         createChild: function(config, insertBefore, returnDom){
9156             config = config || {tag:'div'};
9157             if(insertBefore){
9158                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9159             }
9160             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9161         },
9162
9163         /**
9164          * Appends this element to the passed element
9165          * @param {String/HTMLElement/Element} el The new parent element
9166          * @return {Roo.Element} this
9167          */
9168         appendTo: function(el){
9169             el = Roo.getDom(el);
9170             el.appendChild(this.dom);
9171             return this;
9172         },
9173
9174         /**
9175          * Inserts this element before the passed element in the DOM
9176          * @param {String/HTMLElement/Element} el The element to insert before
9177          * @return {Roo.Element} this
9178          */
9179         insertBefore: function(el){
9180             el = Roo.getDom(el);
9181             el.parentNode.insertBefore(this.dom, el);
9182             return this;
9183         },
9184
9185         /**
9186          * Inserts this element after the passed element in the DOM
9187          * @param {String/HTMLElement/Element} el The element to insert after
9188          * @return {Roo.Element} this
9189          */
9190         insertAfter: function(el){
9191             el = Roo.getDom(el);
9192             el.parentNode.insertBefore(this.dom, el.nextSibling);
9193             return this;
9194         },
9195
9196         /**
9197          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9198          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9199          * @return {Roo.Element} The new child
9200          */
9201         insertFirst: function(el, returnDom){
9202             el = el || {};
9203             if(typeof el == 'object' && !el.nodeType){ // dh config
9204                 return this.createChild(el, this.dom.firstChild, returnDom);
9205             }else{
9206                 el = Roo.getDom(el);
9207                 this.dom.insertBefore(el, this.dom.firstChild);
9208                 return !returnDom ? Roo.get(el) : el;
9209             }
9210         },
9211
9212         /**
9213          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9214          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9215          * @param {String} where (optional) 'before' or 'after' defaults to before
9216          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9217          * @return {Roo.Element} the inserted Element
9218          */
9219         insertSibling: function(el, where, returnDom){
9220             where = where ? where.toLowerCase() : 'before';
9221             el = el || {};
9222             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9223
9224             if(typeof el == 'object' && !el.nodeType){ // dh config
9225                 if(where == 'after' && !this.dom.nextSibling){
9226                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9227                 }else{
9228                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9229                 }
9230
9231             }else{
9232                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9233                             where == 'before' ? this.dom : this.dom.nextSibling);
9234                 if(!returnDom){
9235                     rt = Roo.get(rt);
9236                 }
9237             }
9238             return rt;
9239         },
9240
9241         /**
9242          * Creates and wraps this element with another element
9243          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {HTMLElement/Element} The newly created wrapper element
9246          */
9247         wrap: function(config, returnDom){
9248             if(!config){
9249                 config = {tag: "div"};
9250             }
9251             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9252             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9253             return newEl;
9254         },
9255
9256         /**
9257          * Replaces the passed element with this element
9258          * @param {String/HTMLElement/Element} el The element to replace
9259          * @return {Roo.Element} this
9260          */
9261         replace: function(el){
9262             el = Roo.get(el);
9263             this.insertBefore(el);
9264             el.remove();
9265             return this;
9266         },
9267
9268         /**
9269          * Inserts an html fragment into this element
9270          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9271          * @param {String} html The HTML fragment
9272          * @param {Boolean} returnEl True to return an Roo.Element
9273          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9274          */
9275         insertHtml : function(where, html, returnEl){
9276             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9277             return returnEl ? Roo.get(el) : el;
9278         },
9279
9280         /**
9281          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9282          * @param {Object} o The object with the attributes
9283          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9284          * @return {Roo.Element} this
9285          */
9286         set : function(o, useSet){
9287             var el = this.dom;
9288             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9289             for(var attr in o){
9290                 if(attr == "style" || typeof o[attr] == "function") continue;
9291                 if(attr=="cls"){
9292                     el.className = o["cls"];
9293                 }else{
9294                     if(useSet) el.setAttribute(attr, o[attr]);
9295                     else el[attr] = o[attr];
9296                 }
9297             }
9298             if(o.style){
9299                 Roo.DomHelper.applyStyles(el, o.style);
9300             }
9301             return this;
9302         },
9303
9304         /**
9305          * Convenience method for constructing a KeyMap
9306          * @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:
9307          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9308          * @param {Function} fn The function to call
9309          * @param {Object} scope (optional) The scope of the function
9310          * @return {Roo.KeyMap} The KeyMap created
9311          */
9312         addKeyListener : function(key, fn, scope){
9313             var config;
9314             if(typeof key != "object" || key instanceof Array){
9315                 config = {
9316                     key: key,
9317                     fn: fn,
9318                     scope: scope
9319                 };
9320             }else{
9321                 config = {
9322                     key : key.key,
9323                     shift : key.shift,
9324                     ctrl : key.ctrl,
9325                     alt : key.alt,
9326                     fn: fn,
9327                     scope: scope
9328                 };
9329             }
9330             return new Roo.KeyMap(this, config);
9331         },
9332
9333         /**
9334          * Creates a KeyMap for this element
9335          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9336          * @return {Roo.KeyMap} The KeyMap created
9337          */
9338         addKeyMap : function(config){
9339             return new Roo.KeyMap(this, config);
9340         },
9341
9342         /**
9343          * Returns true if this element is scrollable.
9344          * @return {Boolean}
9345          */
9346          isScrollable : function(){
9347             var dom = this.dom;
9348             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9349         },
9350
9351         /**
9352          * 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().
9353          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9354          * @param {Number} value The new scroll value
9355          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9356          * @return {Element} this
9357          */
9358
9359         scrollTo : function(side, value, animate){
9360             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9361             if(!animate || !A){
9362                 this.dom[prop] = value;
9363             }else{
9364                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9365                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9366             }
9367             return this;
9368         },
9369
9370         /**
9371          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9372          * within this element's scrollable range.
9373          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9374          * @param {Number} distance How far to scroll the element in pixels
9375          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9376          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9377          * was scrolled as far as it could go.
9378          */
9379          scroll : function(direction, distance, animate){
9380              if(!this.isScrollable()){
9381                  return;
9382              }
9383              var el = this.dom;
9384              var l = el.scrollLeft, t = el.scrollTop;
9385              var w = el.scrollWidth, h = el.scrollHeight;
9386              var cw = el.clientWidth, ch = el.clientHeight;
9387              direction = direction.toLowerCase();
9388              var scrolled = false;
9389              var a = this.preanim(arguments, 2);
9390              switch(direction){
9391                  case "l":
9392                  case "left":
9393                      if(w - l > cw){
9394                          var v = Math.min(l + distance, w-cw);
9395                          this.scrollTo("left", v, a);
9396                          scrolled = true;
9397                      }
9398                      break;
9399                 case "r":
9400                 case "right":
9401                      if(l > 0){
9402                          var v = Math.max(l - distance, 0);
9403                          this.scrollTo("left", v, a);
9404                          scrolled = true;
9405                      }
9406                      break;
9407                 case "t":
9408                 case "top":
9409                 case "up":
9410                      if(t > 0){
9411                          var v = Math.max(t - distance, 0);
9412                          this.scrollTo("top", v, a);
9413                          scrolled = true;
9414                      }
9415                      break;
9416                 case "b":
9417                 case "bottom":
9418                 case "down":
9419                      if(h - t > ch){
9420                          var v = Math.min(t + distance, h-ch);
9421                          this.scrollTo("top", v, a);
9422                          scrolled = true;
9423                      }
9424                      break;
9425              }
9426              return scrolled;
9427         },
9428
9429         /**
9430          * Translates the passed page coordinates into left/top css values for this element
9431          * @param {Number/Array} x The page x or an array containing [x, y]
9432          * @param {Number} y The page y
9433          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9434          */
9435         translatePoints : function(x, y){
9436             if(typeof x == 'object' || x instanceof Array){
9437                 y = x[1]; x = x[0];
9438             }
9439             var p = this.getStyle('position');
9440             var o = this.getXY();
9441
9442             var l = parseInt(this.getStyle('left'), 10);
9443             var t = parseInt(this.getStyle('top'), 10);
9444
9445             if(isNaN(l)){
9446                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9447             }
9448             if(isNaN(t)){
9449                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9450             }
9451
9452             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9453         },
9454
9455         /**
9456          * Returns the current scroll position of the element.
9457          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9458          */
9459         getScroll : function(){
9460             var d = this.dom, doc = document;
9461             if(d == doc || d == doc.body){
9462                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9463                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9464                 return {left: l, top: t};
9465             }else{
9466                 return {left: d.scrollLeft, top: d.scrollTop};
9467             }
9468         },
9469
9470         /**
9471          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9472          * are convert to standard 6 digit hex color.
9473          * @param {String} attr The css attribute
9474          * @param {String} defaultValue The default value to use when a valid color isn't found
9475          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9476          * YUI color anims.
9477          */
9478         getColor : function(attr, defaultValue, prefix){
9479             var v = this.getStyle(attr);
9480             if(!v || v == "transparent" || v == "inherit") {
9481                 return defaultValue;
9482             }
9483             var color = typeof prefix == "undefined" ? "#" : prefix;
9484             if(v.substr(0, 4) == "rgb("){
9485                 var rvs = v.slice(4, v.length -1).split(",");
9486                 for(var i = 0; i < 3; i++){
9487                     var h = parseInt(rvs[i]).toString(16);
9488                     if(h < 16){
9489                         h = "0" + h;
9490                     }
9491                     color += h;
9492                 }
9493             } else {
9494                 if(v.substr(0, 1) == "#"){
9495                     if(v.length == 4) {
9496                         for(var i = 1; i < 4; i++){
9497                             var c = v.charAt(i);
9498                             color +=  c + c;
9499                         }
9500                     }else if(v.length == 7){
9501                         color += v.substr(1);
9502                     }
9503                 }
9504             }
9505             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9506         },
9507
9508         /**
9509          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9510          * gradient background, rounded corners and a 4-way shadow.
9511          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9512          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9513          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9514          * @return {Roo.Element} this
9515          */
9516         boxWrap : function(cls){
9517             cls = cls || 'x-box';
9518             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9519             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9520             return el;
9521         },
9522
9523         /**
9524          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9525          * @param {String} namespace The namespace in which to look for the attribute
9526          * @param {String} name The attribute name
9527          * @return {String} The attribute value
9528          */
9529         getAttributeNS : Roo.isIE ? function(ns, name){
9530             var d = this.dom;
9531             var type = typeof d[ns+":"+name];
9532             if(type != 'undefined' && type != 'unknown'){
9533                 return d[ns+":"+name];
9534             }
9535             return d[name];
9536         } : function(ns, name){
9537             var d = this.dom;
9538             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9539         }
9540     };
9541
9542     var ep = El.prototype;
9543
9544     /**
9545      * Appends an event handler (Shorthand for addListener)
9546      * @param {String}   eventName     The type of event to append
9547      * @param {Function} fn        The method the event invokes
9548      * @param {Object} scope       (optional) The scope (this object) of the fn
9549      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9550      * @method
9551      */
9552     ep.on = ep.addListener;
9553         // backwards compat
9554     ep.mon = ep.addListener;
9555
9556     /**
9557      * Removes an event handler from this element (shorthand for removeListener)
9558      * @param {String} eventName the type of event to remove
9559      * @param {Function} fn the method the event invokes
9560      * @return {Roo.Element} this
9561      * @method
9562      */
9563     ep.un = ep.removeListener;
9564
9565     /**
9566      * true to automatically adjust width and height settings for box-model issues (default to true)
9567      */
9568     ep.autoBoxAdjust = true;
9569
9570     // private
9571     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9572
9573     // private
9574     El.addUnits = function(v, defaultUnit){
9575         if(v === "" || v == "auto"){
9576             return v;
9577         }
9578         if(v === undefined){
9579             return '';
9580         }
9581         if(typeof v == "number" || !El.unitPattern.test(v)){
9582             return v + (defaultUnit || 'px');
9583         }
9584         return v;
9585     };
9586
9587     // special markup used throughout Roo when box wrapping elements
9588     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>';
9589     /**
9590      * Visibility mode constant - Use visibility to hide element
9591      * @static
9592      * @type Number
9593      */
9594     El.VISIBILITY = 1;
9595     /**
9596      * Visibility mode constant - Use display to hide element
9597      * @static
9598      * @type Number
9599      */
9600     El.DISPLAY = 2;
9601
9602     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9603     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9604     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9605
9606
9607
9608     /**
9609      * @private
9610      */
9611     El.cache = {};
9612
9613     var docEl;
9614
9615     /**
9616      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9617      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9618      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9619      * @return {Element} The Element object
9620      * @static
9621      */
9622     El.get = function(el){
9623         var ex, elm, id;
9624         if(!el){ return null; }
9625         if(typeof el == "string"){ // element id
9626             if(!(elm = document.getElementById(el))){
9627                 return null;
9628             }
9629             if(ex = El.cache[el]){
9630                 ex.dom = elm;
9631             }else{
9632                 ex = El.cache[el] = new El(elm);
9633             }
9634             return ex;
9635         }else if(el.tagName){ // dom element
9636             if(!(id = el.id)){
9637                 id = Roo.id(el);
9638             }
9639             if(ex = El.cache[id]){
9640                 ex.dom = el;
9641             }else{
9642                 ex = El.cache[id] = new El(el);
9643             }
9644             return ex;
9645         }else if(el instanceof El){
9646             if(el != docEl){
9647                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9648                                                               // catch case where it hasn't been appended
9649                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9650             }
9651             return el;
9652         }else if(el.isComposite){
9653             return el;
9654         }else if(el instanceof Array){
9655             return El.select(el);
9656         }else if(el == document){
9657             // create a bogus element object representing the document object
9658             if(!docEl){
9659                 var f = function(){};
9660                 f.prototype = El.prototype;
9661                 docEl = new f();
9662                 docEl.dom = document;
9663             }
9664             return docEl;
9665         }
9666         return null;
9667     };
9668
9669     // private
9670     El.uncache = function(el){
9671         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9672             if(a[i]){
9673                 delete El.cache[a[i].id || a[i]];
9674             }
9675         }
9676     };
9677
9678     // private
9679     // Garbage collection - uncache elements/purge listeners on orphaned elements
9680     // so we don't hold a reference and cause the browser to retain them
9681     El.garbageCollect = function(){
9682         if(!Roo.enableGarbageCollector){
9683             clearInterval(El.collectorThread);
9684             return;
9685         }
9686         for(var eid in El.cache){
9687             var el = El.cache[eid], d = el.dom;
9688             // -------------------------------------------------------
9689             // Determining what is garbage:
9690             // -------------------------------------------------------
9691             // !d
9692             // dom node is null, definitely garbage
9693             // -------------------------------------------------------
9694             // !d.parentNode
9695             // no parentNode == direct orphan, definitely garbage
9696             // -------------------------------------------------------
9697             // !d.offsetParent && !document.getElementById(eid)
9698             // display none elements have no offsetParent so we will
9699             // also try to look it up by it's id. However, check
9700             // offsetParent first so we don't do unneeded lookups.
9701             // This enables collection of elements that are not orphans
9702             // directly, but somewhere up the line they have an orphan
9703             // parent.
9704             // -------------------------------------------------------
9705             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9706                 delete El.cache[eid];
9707                 if(d && Roo.enableListenerCollection){
9708                     E.purgeElement(d);
9709                 }
9710             }
9711         }
9712     }
9713     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9714
9715
9716     // dom is optional
9717     El.Flyweight = function(dom){
9718         this.dom = dom;
9719     };
9720     El.Flyweight.prototype = El.prototype;
9721
9722     El._flyweights = {};
9723     /**
9724      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9725      * the dom node can be overwritten by other code.
9726      * @param {String/HTMLElement} el The dom node or id
9727      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9728      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9729      * @static
9730      * @return {Element} The shared Element object
9731      */
9732     El.fly = function(el, named){
9733         named = named || '_global';
9734         el = Roo.getDom(el);
9735         if(!el){
9736             return null;
9737         }
9738         if(!El._flyweights[named]){
9739             El._flyweights[named] = new El.Flyweight();
9740         }
9741         El._flyweights[named].dom = el;
9742         return El._flyweights[named];
9743     };
9744
9745     /**
9746      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9747      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9748      * Shorthand of {@link Roo.Element#get}
9749      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9750      * @return {Element} The Element object
9751      * @member Roo
9752      * @method get
9753      */
9754     Roo.get = El.get;
9755     /**
9756      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9757      * the dom node can be overwritten by other code.
9758      * Shorthand of {@link Roo.Element#fly}
9759      * @param {String/HTMLElement} el The dom node or id
9760      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9761      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9762      * @static
9763      * @return {Element} The shared Element object
9764      * @member Roo
9765      * @method fly
9766      */
9767     Roo.fly = El.fly;
9768
9769     // speedy lookup for elements never to box adjust
9770     var noBoxAdjust = Roo.isStrict ? {
9771         select:1
9772     } : {
9773         input:1, select:1, textarea:1
9774     };
9775     if(Roo.isIE || Roo.isGecko){
9776         noBoxAdjust['button'] = 1;
9777     }
9778
9779
9780     Roo.EventManager.on(window, 'unload', function(){
9781         delete El.cache;
9782         delete El._flyweights;
9783     });
9784 })();
9785
9786
9787
9788
9789 if(Roo.DomQuery){
9790     Roo.Element.selectorFunction = Roo.DomQuery.select;
9791 }
9792
9793 Roo.Element.select = function(selector, unique, root){
9794     var els;
9795     if(typeof selector == "string"){
9796         els = Roo.Element.selectorFunction(selector, root);
9797     }else if(selector.length !== undefined){
9798         els = selector;
9799     }else{
9800         throw "Invalid selector";
9801     }
9802     if(unique === true){
9803         return new Roo.CompositeElement(els);
9804     }else{
9805         return new Roo.CompositeElementLite(els);
9806     }
9807 };
9808 /**
9809  * Selects elements based on the passed CSS selector to enable working on them as 1.
9810  * @param {String/Array} selector The CSS selector or an array of elements
9811  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9812  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9813  * @return {CompositeElementLite/CompositeElement}
9814  * @member Roo
9815  * @method select
9816  */
9817 Roo.select = Roo.Element.select;
9818
9819
9820
9821
9822
9823
9824
9825
9826
9827
9828
9829
9830
9831
9832 /*
9833  * Based on:
9834  * Ext JS Library 1.1.1
9835  * Copyright(c) 2006-2007, Ext JS, LLC.
9836  *
9837  * Originally Released Under LGPL - original licence link has changed is not relivant.
9838  *
9839  * Fork - LGPL
9840  * <script type="text/javascript">
9841  */
9842
9843
9844
9845 //Notifies Element that fx methods are available
9846 Roo.enableFx = true;
9847
9848 /**
9849  * @class Roo.Fx
9850  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9851  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9852  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9853  * Element effects to work.</p><br/>
9854  *
9855  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9856  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9857  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9858  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9859  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9860  * expected results and should be done with care.</p><br/>
9861  *
9862  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9863  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9864 <pre>
9865 Value  Description
9866 -----  -----------------------------
9867 tl     The top left corner
9868 t      The center of the top edge
9869 tr     The top right corner
9870 l      The center of the left edge
9871 r      The center of the right edge
9872 bl     The bottom left corner
9873 b      The center of the bottom edge
9874 br     The bottom right corner
9875 </pre>
9876  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9877  * below are common options that can be passed to any Fx method.</b>
9878  * @cfg {Function} callback A function called when the effect is finished
9879  * @cfg {Object} scope The scope of the effect function
9880  * @cfg {String} easing A valid Easing value for the effect
9881  * @cfg {String} afterCls A css class to apply after the effect
9882  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9883  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9884  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9885  * effects that end with the element being visually hidden, ignored otherwise)
9886  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9887  * a function which returns such a specification that will be applied to the Element after the effect finishes
9888  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9889  * @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
9890  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9891  */
9892 Roo.Fx = {
9893         /**
9894          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9895          * origin for the slide effect.  This function automatically handles wrapping the element with
9896          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9897          * Usage:
9898          *<pre><code>
9899 // default: slide the element in from the top
9900 el.slideIn();
9901
9902 // custom: slide the element in from the right with a 2-second duration
9903 el.slideIn('r', { duration: 2 });
9904
9905 // common config options shown with default values
9906 el.slideIn('t', {
9907     easing: 'easeOut',
9908     duration: .5
9909 });
9910 </code></pre>
9911          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9912          * @param {Object} options (optional) Object literal with any of the Fx config options
9913          * @return {Roo.Element} The Element
9914          */
9915     slideIn : function(anchor, o){
9916         var el = this.getFxEl();
9917         o = o || {};
9918
9919         el.queueFx(o, function(){
9920
9921             anchor = anchor || "t";
9922
9923             // fix display to visibility
9924             this.fixDisplay();
9925
9926             // restore values after effect
9927             var r = this.getFxRestore();
9928             var b = this.getBox();
9929             // fixed size for slide
9930             this.setSize(b);
9931
9932             // wrap if needed
9933             var wrap = this.fxWrap(r.pos, o, "hidden");
9934
9935             var st = this.dom.style;
9936             st.visibility = "visible";
9937             st.position = "absolute";
9938
9939             // clear out temp styles after slide and unwrap
9940             var after = function(){
9941                 el.fxUnwrap(wrap, r.pos, o);
9942                 st.width = r.width;
9943                 st.height = r.height;
9944                 el.afterFx(o);
9945             };
9946             // time to calc the positions
9947             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9948
9949             switch(anchor.toLowerCase()){
9950                 case "t":
9951                     wrap.setSize(b.width, 0);
9952                     st.left = st.bottom = "0";
9953                     a = {height: bh};
9954                 break;
9955                 case "l":
9956                     wrap.setSize(0, b.height);
9957                     st.right = st.top = "0";
9958                     a = {width: bw};
9959                 break;
9960                 case "r":
9961                     wrap.setSize(0, b.height);
9962                     wrap.setX(b.right);
9963                     st.left = st.top = "0";
9964                     a = {width: bw, points: pt};
9965                 break;
9966                 case "b":
9967                     wrap.setSize(b.width, 0);
9968                     wrap.setY(b.bottom);
9969                     st.left = st.top = "0";
9970                     a = {height: bh, points: pt};
9971                 break;
9972                 case "tl":
9973                     wrap.setSize(0, 0);
9974                     st.right = st.bottom = "0";
9975                     a = {width: bw, height: bh};
9976                 break;
9977                 case "bl":
9978                     wrap.setSize(0, 0);
9979                     wrap.setY(b.y+b.height);
9980                     st.right = st.top = "0";
9981                     a = {width: bw, height: bh, points: pt};
9982                 break;
9983                 case "br":
9984                     wrap.setSize(0, 0);
9985                     wrap.setXY([b.right, b.bottom]);
9986                     st.left = st.top = "0";
9987                     a = {width: bw, height: bh, points: pt};
9988                 break;
9989                 case "tr":
9990                     wrap.setSize(0, 0);
9991                     wrap.setX(b.x+b.width);
9992                     st.left = st.bottom = "0";
9993                     a = {width: bw, height: bh, points: pt};
9994                 break;
9995             }
9996             this.dom.style.visibility = "visible";
9997             wrap.show();
9998
9999             arguments.callee.anim = wrap.fxanim(a,
10000                 o,
10001                 'motion',
10002                 .5,
10003                 'easeOut', after);
10004         });
10005         return this;
10006     },
10007     
10008         /**
10009          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10010          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10011          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10012          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10013          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10014          * Usage:
10015          *<pre><code>
10016 // default: slide the element out to the top
10017 el.slideOut();
10018
10019 // custom: slide the element out to the right with a 2-second duration
10020 el.slideOut('r', { duration: 2 });
10021
10022 // common config options shown with default values
10023 el.slideOut('t', {
10024     easing: 'easeOut',
10025     duration: .5,
10026     remove: false,
10027     useDisplay: false
10028 });
10029 </code></pre>
10030          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10031          * @param {Object} options (optional) Object literal with any of the Fx config options
10032          * @return {Roo.Element} The Element
10033          */
10034     slideOut : function(anchor, o){
10035         var el = this.getFxEl();
10036         o = o || {};
10037
10038         el.queueFx(o, function(){
10039
10040             anchor = anchor || "t";
10041
10042             // restore values after effect
10043             var r = this.getFxRestore();
10044             
10045             var b = this.getBox();
10046             // fixed size for slide
10047             this.setSize(b);
10048
10049             // wrap if needed
10050             var wrap = this.fxWrap(r.pos, o, "visible");
10051
10052             var st = this.dom.style;
10053             st.visibility = "visible";
10054             st.position = "absolute";
10055
10056             wrap.setSize(b);
10057
10058             var after = function(){
10059                 if(o.useDisplay){
10060                     el.setDisplayed(false);
10061                 }else{
10062                     el.hide();
10063                 }
10064
10065                 el.fxUnwrap(wrap, r.pos, o);
10066
10067                 st.width = r.width;
10068                 st.height = r.height;
10069
10070                 el.afterFx(o);
10071             };
10072
10073             var a, zero = {to: 0};
10074             switch(anchor.toLowerCase()){
10075                 case "t":
10076                     st.left = st.bottom = "0";
10077                     a = {height: zero};
10078                 break;
10079                 case "l":
10080                     st.right = st.top = "0";
10081                     a = {width: zero};
10082                 break;
10083                 case "r":
10084                     st.left = st.top = "0";
10085                     a = {width: zero, points: {to:[b.right, b.y]}};
10086                 break;
10087                 case "b":
10088                     st.left = st.top = "0";
10089                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10090                 break;
10091                 case "tl":
10092                     st.right = st.bottom = "0";
10093                     a = {width: zero, height: zero};
10094                 break;
10095                 case "bl":
10096                     st.right = st.top = "0";
10097                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10098                 break;
10099                 case "br":
10100                     st.left = st.top = "0";
10101                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10102                 break;
10103                 case "tr":
10104                     st.left = st.bottom = "0";
10105                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10106                 break;
10107             }
10108
10109             arguments.callee.anim = wrap.fxanim(a,
10110                 o,
10111                 'motion',
10112                 .5,
10113                 "easeOut", after);
10114         });
10115         return this;
10116     },
10117
10118         /**
10119          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10120          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10121          * The element must be removed from the DOM using the 'remove' config option if desired.
10122          * Usage:
10123          *<pre><code>
10124 // default
10125 el.puff();
10126
10127 // common config options shown with default values
10128 el.puff({
10129     easing: 'easeOut',
10130     duration: .5,
10131     remove: false,
10132     useDisplay: false
10133 });
10134 </code></pre>
10135          * @param {Object} options (optional) Object literal with any of the Fx config options
10136          * @return {Roo.Element} The Element
10137          */
10138     puff : function(o){
10139         var el = this.getFxEl();
10140         o = o || {};
10141
10142         el.queueFx(o, function(){
10143             this.clearOpacity();
10144             this.show();
10145
10146             // restore values after effect
10147             var r = this.getFxRestore();
10148             var st = this.dom.style;
10149
10150             var after = function(){
10151                 if(o.useDisplay){
10152                     el.setDisplayed(false);
10153                 }else{
10154                     el.hide();
10155                 }
10156
10157                 el.clearOpacity();
10158
10159                 el.setPositioning(r.pos);
10160                 st.width = r.width;
10161                 st.height = r.height;
10162                 st.fontSize = '';
10163                 el.afterFx(o);
10164             };
10165
10166             var width = this.getWidth();
10167             var height = this.getHeight();
10168
10169             arguments.callee.anim = this.fxanim({
10170                     width : {to: this.adjustWidth(width * 2)},
10171                     height : {to: this.adjustHeight(height * 2)},
10172                     points : {by: [-(width * .5), -(height * .5)]},
10173                     opacity : {to: 0},
10174                     fontSize: {to:200, unit: "%"}
10175                 },
10176                 o,
10177                 'motion',
10178                 .5,
10179                 "easeOut", after);
10180         });
10181         return this;
10182     },
10183
10184         /**
10185          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10186          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10187          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10188          * Usage:
10189          *<pre><code>
10190 // default
10191 el.switchOff();
10192
10193 // all config options shown with default values
10194 el.switchOff({
10195     easing: 'easeIn',
10196     duration: .3,
10197     remove: false,
10198     useDisplay: false
10199 });
10200 </code></pre>
10201          * @param {Object} options (optional) Object literal with any of the Fx config options
10202          * @return {Roo.Element} The Element
10203          */
10204     switchOff : function(o){
10205         var el = this.getFxEl();
10206         o = o || {};
10207
10208         el.queueFx(o, function(){
10209             this.clearOpacity();
10210             this.clip();
10211
10212             // restore values after effect
10213             var r = this.getFxRestore();
10214             var st = this.dom.style;
10215
10216             var after = function(){
10217                 if(o.useDisplay){
10218                     el.setDisplayed(false);
10219                 }else{
10220                     el.hide();
10221                 }
10222
10223                 el.clearOpacity();
10224                 el.setPositioning(r.pos);
10225                 st.width = r.width;
10226                 st.height = r.height;
10227
10228                 el.afterFx(o);
10229             };
10230
10231             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10232                 this.clearOpacity();
10233                 (function(){
10234                     this.fxanim({
10235                         height:{to:1},
10236                         points:{by:[0, this.getHeight() * .5]}
10237                     }, o, 'motion', 0.3, 'easeIn', after);
10238                 }).defer(100, this);
10239             });
10240         });
10241         return this;
10242     },
10243
10244     /**
10245      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10246      * changed using the "attr" config option) and then fading back to the original color. If no original
10247      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10248      * Usage:
10249 <pre><code>
10250 // default: highlight background to yellow
10251 el.highlight();
10252
10253 // custom: highlight foreground text to blue for 2 seconds
10254 el.highlight("0000ff", { attr: 'color', duration: 2 });
10255
10256 // common config options shown with default values
10257 el.highlight("ffff9c", {
10258     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10259     endColor: (current color) or "ffffff",
10260     easing: 'easeIn',
10261     duration: 1
10262 });
10263 </code></pre>
10264      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10265      * @param {Object} options (optional) Object literal with any of the Fx config options
10266      * @return {Roo.Element} The Element
10267      */ 
10268     highlight : function(color, o){
10269         var el = this.getFxEl();
10270         o = o || {};
10271
10272         el.queueFx(o, function(){
10273             color = color || "ffff9c";
10274             attr = o.attr || "backgroundColor";
10275
10276             this.clearOpacity();
10277             this.show();
10278
10279             var origColor = this.getColor(attr);
10280             var restoreColor = this.dom.style[attr];
10281             endColor = (o.endColor || origColor) || "ffffff";
10282
10283             var after = function(){
10284                 el.dom.style[attr] = restoreColor;
10285                 el.afterFx(o);
10286             };
10287
10288             var a = {};
10289             a[attr] = {from: color, to: endColor};
10290             arguments.callee.anim = this.fxanim(a,
10291                 o,
10292                 'color',
10293                 1,
10294                 'easeIn', after);
10295         });
10296         return this;
10297     },
10298
10299    /**
10300     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10301     * Usage:
10302 <pre><code>
10303 // default: a single light blue ripple
10304 el.frame();
10305
10306 // custom: 3 red ripples lasting 3 seconds total
10307 el.frame("ff0000", 3, { duration: 3 });
10308
10309 // common config options shown with default values
10310 el.frame("C3DAF9", 1, {
10311     duration: 1 //duration of entire animation (not each individual ripple)
10312     // Note: Easing is not configurable and will be ignored if included
10313 });
10314 </code></pre>
10315     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10316     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10317     * @param {Object} options (optional) Object literal with any of the Fx config options
10318     * @return {Roo.Element} The Element
10319     */
10320     frame : function(color, count, o){
10321         var el = this.getFxEl();
10322         o = o || {};
10323
10324         el.queueFx(o, function(){
10325             color = color || "#C3DAF9";
10326             if(color.length == 6){
10327                 color = "#" + color;
10328             }
10329             count = count || 1;
10330             duration = o.duration || 1;
10331             this.show();
10332
10333             var b = this.getBox();
10334             var animFn = function(){
10335                 var proxy = this.createProxy({
10336
10337                      style:{
10338                         visbility:"hidden",
10339                         position:"absolute",
10340                         "z-index":"35000", // yee haw
10341                         border:"0px solid " + color
10342                      }
10343                   });
10344                 var scale = Roo.isBorderBox ? 2 : 1;
10345                 proxy.animate({
10346                     top:{from:b.y, to:b.y - 20},
10347                     left:{from:b.x, to:b.x - 20},
10348                     borderWidth:{from:0, to:10},
10349                     opacity:{from:1, to:0},
10350                     height:{from:b.height, to:(b.height + (20*scale))},
10351                     width:{from:b.width, to:(b.width + (20*scale))}
10352                 }, duration, function(){
10353                     proxy.remove();
10354                 });
10355                 if(--count > 0){
10356                      animFn.defer((duration/2)*1000, this);
10357                 }else{
10358                     el.afterFx(o);
10359                 }
10360             };
10361             animFn.call(this);
10362         });
10363         return this;
10364     },
10365
10366    /**
10367     * Creates a pause before any subsequent queued effects begin.  If there are
10368     * no effects queued after the pause it will have no effect.
10369     * Usage:
10370 <pre><code>
10371 el.pause(1);
10372 </code></pre>
10373     * @param {Number} seconds The length of time to pause (in seconds)
10374     * @return {Roo.Element} The Element
10375     */
10376     pause : function(seconds){
10377         var el = this.getFxEl();
10378         var o = {};
10379
10380         el.queueFx(o, function(){
10381             setTimeout(function(){
10382                 el.afterFx(o);
10383             }, seconds * 1000);
10384         });
10385         return this;
10386     },
10387
10388    /**
10389     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10390     * using the "endOpacity" config option.
10391     * Usage:
10392 <pre><code>
10393 // default: fade in from opacity 0 to 100%
10394 el.fadeIn();
10395
10396 // custom: fade in from opacity 0 to 75% over 2 seconds
10397 el.fadeIn({ endOpacity: .75, duration: 2});
10398
10399 // common config options shown with default values
10400 el.fadeIn({
10401     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10402     easing: 'easeOut',
10403     duration: .5
10404 });
10405 </code></pre>
10406     * @param {Object} options (optional) Object literal with any of the Fx config options
10407     * @return {Roo.Element} The Element
10408     */
10409     fadeIn : function(o){
10410         var el = this.getFxEl();
10411         o = o || {};
10412         el.queueFx(o, function(){
10413             this.setOpacity(0);
10414             this.fixDisplay();
10415             this.dom.style.visibility = 'visible';
10416             var to = o.endOpacity || 1;
10417             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10418                 o, null, .5, "easeOut", function(){
10419                 if(to == 1){
10420                     this.clearOpacity();
10421                 }
10422                 el.afterFx(o);
10423             });
10424         });
10425         return this;
10426     },
10427
10428    /**
10429     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10430     * using the "endOpacity" config option.
10431     * Usage:
10432 <pre><code>
10433 // default: fade out from the element's current opacity to 0
10434 el.fadeOut();
10435
10436 // custom: fade out from the element's current opacity to 25% over 2 seconds
10437 el.fadeOut({ endOpacity: .25, duration: 2});
10438
10439 // common config options shown with default values
10440 el.fadeOut({
10441     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10442     easing: 'easeOut',
10443     duration: .5
10444     remove: false,
10445     useDisplay: false
10446 });
10447 </code></pre>
10448     * @param {Object} options (optional) Object literal with any of the Fx config options
10449     * @return {Roo.Element} The Element
10450     */
10451     fadeOut : function(o){
10452         var el = this.getFxEl();
10453         o = o || {};
10454         el.queueFx(o, function(){
10455             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10456                 o, null, .5, "easeOut", function(){
10457                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10458                      this.dom.style.display = "none";
10459                 }else{
10460                      this.dom.style.visibility = "hidden";
10461                 }
10462                 this.clearOpacity();
10463                 el.afterFx(o);
10464             });
10465         });
10466         return this;
10467     },
10468
10469    /**
10470     * Animates the transition of an element's dimensions from a starting height/width
10471     * to an ending height/width.
10472     * Usage:
10473 <pre><code>
10474 // change height and width to 100x100 pixels
10475 el.scale(100, 100);
10476
10477 // common config options shown with default values.  The height and width will default to
10478 // the element's existing values if passed as null.
10479 el.scale(
10480     [element's width],
10481     [element's height], {
10482     easing: 'easeOut',
10483     duration: .35
10484 });
10485 </code></pre>
10486     * @param {Number} width  The new width (pass undefined to keep the original width)
10487     * @param {Number} height  The new height (pass undefined to keep the original height)
10488     * @param {Object} options (optional) Object literal with any of the Fx config options
10489     * @return {Roo.Element} The Element
10490     */
10491     scale : function(w, h, o){
10492         this.shift(Roo.apply({}, o, {
10493             width: w,
10494             height: h
10495         }));
10496         return this;
10497     },
10498
10499    /**
10500     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10501     * Any of these properties not specified in the config object will not be changed.  This effect 
10502     * requires that at least one new dimension, position or opacity setting must be passed in on
10503     * the config object in order for the function to have any effect.
10504     * Usage:
10505 <pre><code>
10506 // slide the element horizontally to x position 200 while changing the height and opacity
10507 el.shift({ x: 200, height: 50, opacity: .8 });
10508
10509 // common config options shown with default values.
10510 el.shift({
10511     width: [element's width],
10512     height: [element's height],
10513     x: [element's x position],
10514     y: [element's y position],
10515     opacity: [element's opacity],
10516     easing: 'easeOut',
10517     duration: .35
10518 });
10519 </code></pre>
10520     * @param {Object} options  Object literal with any of the Fx config options
10521     * @return {Roo.Element} The Element
10522     */
10523     shift : function(o){
10524         var el = this.getFxEl();
10525         o = o || {};
10526         el.queueFx(o, function(){
10527             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10528             if(w !== undefined){
10529                 a.width = {to: this.adjustWidth(w)};
10530             }
10531             if(h !== undefined){
10532                 a.height = {to: this.adjustHeight(h)};
10533             }
10534             if(x !== undefined || y !== undefined){
10535                 a.points = {to: [
10536                     x !== undefined ? x : this.getX(),
10537                     y !== undefined ? y : this.getY()
10538                 ]};
10539             }
10540             if(op !== undefined){
10541                 a.opacity = {to: op};
10542             }
10543             if(o.xy !== undefined){
10544                 a.points = {to: o.xy};
10545             }
10546             arguments.callee.anim = this.fxanim(a,
10547                 o, 'motion', .35, "easeOut", function(){
10548                 el.afterFx(o);
10549             });
10550         });
10551         return this;
10552     },
10553
10554         /**
10555          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10556          * ending point of the effect.
10557          * Usage:
10558          *<pre><code>
10559 // default: slide the element downward while fading out
10560 el.ghost();
10561
10562 // custom: slide the element out to the right with a 2-second duration
10563 el.ghost('r', { duration: 2 });
10564
10565 // common config options shown with default values
10566 el.ghost('b', {
10567     easing: 'easeOut',
10568     duration: .5
10569     remove: false,
10570     useDisplay: false
10571 });
10572 </code></pre>
10573          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10574          * @param {Object} options (optional) Object literal with any of the Fx config options
10575          * @return {Roo.Element} The Element
10576          */
10577     ghost : function(anchor, o){
10578         var el = this.getFxEl();
10579         o = o || {};
10580
10581         el.queueFx(o, function(){
10582             anchor = anchor || "b";
10583
10584             // restore values after effect
10585             var r = this.getFxRestore();
10586             var w = this.getWidth(),
10587                 h = this.getHeight();
10588
10589             var st = this.dom.style;
10590
10591             var after = function(){
10592                 if(o.useDisplay){
10593                     el.setDisplayed(false);
10594                 }else{
10595                     el.hide();
10596                 }
10597
10598                 el.clearOpacity();
10599                 el.setPositioning(r.pos);
10600                 st.width = r.width;
10601                 st.height = r.height;
10602
10603                 el.afterFx(o);
10604             };
10605
10606             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10607             switch(anchor.toLowerCase()){
10608                 case "t":
10609                     pt.by = [0, -h];
10610                 break;
10611                 case "l":
10612                     pt.by = [-w, 0];
10613                 break;
10614                 case "r":
10615                     pt.by = [w, 0];
10616                 break;
10617                 case "b":
10618                     pt.by = [0, h];
10619                 break;
10620                 case "tl":
10621                     pt.by = [-w, -h];
10622                 break;
10623                 case "bl":
10624                     pt.by = [-w, h];
10625                 break;
10626                 case "br":
10627                     pt.by = [w, h];
10628                 break;
10629                 case "tr":
10630                     pt.by = [w, -h];
10631                 break;
10632             }
10633
10634             arguments.callee.anim = this.fxanim(a,
10635                 o,
10636                 'motion',
10637                 .5,
10638                 "easeOut", after);
10639         });
10640         return this;
10641     },
10642
10643         /**
10644          * Ensures that all effects queued after syncFx is called on the element are
10645          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10646          * @return {Roo.Element} The Element
10647          */
10648     syncFx : function(){
10649         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10650             block : false,
10651             concurrent : true,
10652             stopFx : false
10653         });
10654         return this;
10655     },
10656
10657         /**
10658          * Ensures that all effects queued after sequenceFx is called on the element are
10659          * run in sequence.  This is the opposite of {@link #syncFx}.
10660          * @return {Roo.Element} The Element
10661          */
10662     sequenceFx : function(){
10663         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10664             block : false,
10665             concurrent : false,
10666             stopFx : false
10667         });
10668         return this;
10669     },
10670
10671         /* @private */
10672     nextFx : function(){
10673         var ef = this.fxQueue[0];
10674         if(ef){
10675             ef.call(this);
10676         }
10677     },
10678
10679         /**
10680          * Returns true if the element has any effects actively running or queued, else returns false.
10681          * @return {Boolean} True if element has active effects, else false
10682          */
10683     hasActiveFx : function(){
10684         return this.fxQueue && this.fxQueue[0];
10685     },
10686
10687         /**
10688          * Stops any running effects and clears the element's internal effects queue if it contains
10689          * any additional effects that haven't started yet.
10690          * @return {Roo.Element} The Element
10691          */
10692     stopFx : function(){
10693         if(this.hasActiveFx()){
10694             var cur = this.fxQueue[0];
10695             if(cur && cur.anim && cur.anim.isAnimated()){
10696                 this.fxQueue = [cur]; // clear out others
10697                 cur.anim.stop(true);
10698             }
10699         }
10700         return this;
10701     },
10702
10703         /* @private */
10704     beforeFx : function(o){
10705         if(this.hasActiveFx() && !o.concurrent){
10706            if(o.stopFx){
10707                this.stopFx();
10708                return true;
10709            }
10710            return false;
10711         }
10712         return true;
10713     },
10714
10715         /**
10716          * Returns true if the element is currently blocking so that no other effect can be queued
10717          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10718          * used to ensure that an effect initiated by a user action runs to completion prior to the
10719          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10720          * @return {Boolean} True if blocking, else false
10721          */
10722     hasFxBlock : function(){
10723         var q = this.fxQueue;
10724         return q && q[0] && q[0].block;
10725     },
10726
10727         /* @private */
10728     queueFx : function(o, fn){
10729         if(!this.fxQueue){
10730             this.fxQueue = [];
10731         }
10732         if(!this.hasFxBlock()){
10733             Roo.applyIf(o, this.fxDefaults);
10734             if(!o.concurrent){
10735                 var run = this.beforeFx(o);
10736                 fn.block = o.block;
10737                 this.fxQueue.push(fn);
10738                 if(run){
10739                     this.nextFx();
10740                 }
10741             }else{
10742                 fn.call(this);
10743             }
10744         }
10745         return this;
10746     },
10747
10748         /* @private */
10749     fxWrap : function(pos, o, vis){
10750         var wrap;
10751         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10752             var wrapXY;
10753             if(o.fixPosition){
10754                 wrapXY = this.getXY();
10755             }
10756             var div = document.createElement("div");
10757             div.style.visibility = vis;
10758             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10759             wrap.setPositioning(pos);
10760             if(wrap.getStyle("position") == "static"){
10761                 wrap.position("relative");
10762             }
10763             this.clearPositioning('auto');
10764             wrap.clip();
10765             wrap.dom.appendChild(this.dom);
10766             if(wrapXY){
10767                 wrap.setXY(wrapXY);
10768             }
10769         }
10770         return wrap;
10771     },
10772
10773         /* @private */
10774     fxUnwrap : function(wrap, pos, o){
10775         this.clearPositioning();
10776         this.setPositioning(pos);
10777         if(!o.wrap){
10778             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10779             wrap.remove();
10780         }
10781     },
10782
10783         /* @private */
10784     getFxRestore : function(){
10785         var st = this.dom.style;
10786         return {pos: this.getPositioning(), width: st.width, height : st.height};
10787     },
10788
10789         /* @private */
10790     afterFx : function(o){
10791         if(o.afterStyle){
10792             this.applyStyles(o.afterStyle);
10793         }
10794         if(o.afterCls){
10795             this.addClass(o.afterCls);
10796         }
10797         if(o.remove === true){
10798             this.remove();
10799         }
10800         Roo.callback(o.callback, o.scope, [this]);
10801         if(!o.concurrent){
10802             this.fxQueue.shift();
10803             this.nextFx();
10804         }
10805     },
10806
10807         /* @private */
10808     getFxEl : function(){ // support for composite element fx
10809         return Roo.get(this.dom);
10810     },
10811
10812         /* @private */
10813     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10814         animType = animType || 'run';
10815         opt = opt || {};
10816         var anim = Roo.lib.Anim[animType](
10817             this.dom, args,
10818             (opt.duration || defaultDur) || .35,
10819             (opt.easing || defaultEase) || 'easeOut',
10820             function(){
10821                 Roo.callback(cb, this);
10822             },
10823             this
10824         );
10825         opt.anim = anim;
10826         return anim;
10827     }
10828 };
10829
10830 // backwords compat
10831 Roo.Fx.resize = Roo.Fx.scale;
10832
10833 //When included, Roo.Fx is automatically applied to Element so that all basic
10834 //effects are available directly via the Element API
10835 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10836  * Based on:
10837  * Ext JS Library 1.1.1
10838  * Copyright(c) 2006-2007, Ext JS, LLC.
10839  *
10840  * Originally Released Under LGPL - original licence link has changed is not relivant.
10841  *
10842  * Fork - LGPL
10843  * <script type="text/javascript">
10844  */
10845
10846
10847 /**
10848  * @class Roo.CompositeElement
10849  * Standard composite class. Creates a Roo.Element for every element in the collection.
10850  * <br><br>
10851  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10852  * actions will be performed on all the elements in this collection.</b>
10853  * <br><br>
10854  * All methods return <i>this</i> and can be chained.
10855  <pre><code>
10856  var els = Roo.select("#some-el div.some-class", true);
10857  // or select directly from an existing element
10858  var el = Roo.get('some-el');
10859  el.select('div.some-class', true);
10860
10861  els.setWidth(100); // all elements become 100 width
10862  els.hide(true); // all elements fade out and hide
10863  // or
10864  els.setWidth(100).hide(true);
10865  </code></pre>
10866  */
10867 Roo.CompositeElement = function(els){
10868     this.elements = [];
10869     this.addElements(els);
10870 };
10871 Roo.CompositeElement.prototype = {
10872     isComposite: true,
10873     addElements : function(els){
10874         if(!els) return this;
10875         if(typeof els == "string"){
10876             els = Roo.Element.selectorFunction(els);
10877         }
10878         var yels = this.elements;
10879         var index = yels.length-1;
10880         for(var i = 0, len = els.length; i < len; i++) {
10881                 yels[++index] = Roo.get(els[i]);
10882         }
10883         return this;
10884     },
10885
10886     /**
10887     * Clears this composite and adds the elements returned by the passed selector.
10888     * @param {String/Array} els A string CSS selector, an array of elements or an element
10889     * @return {CompositeElement} this
10890     */
10891     fill : function(els){
10892         this.elements = [];
10893         this.add(els);
10894         return this;
10895     },
10896
10897     /**
10898     * Filters this composite to only elements that match the passed selector.
10899     * @param {String} selector A string CSS selector
10900     * @return {CompositeElement} this
10901     */
10902     filter : function(selector){
10903         var els = [];
10904         this.each(function(el){
10905             if(el.is(selector)){
10906                 els[els.length] = el.dom;
10907             }
10908         });
10909         this.fill(els);
10910         return this;
10911     },
10912
10913     invoke : function(fn, args){
10914         var els = this.elements;
10915         for(var i = 0, len = els.length; i < len; i++) {
10916                 Roo.Element.prototype[fn].apply(els[i], args);
10917         }
10918         return this;
10919     },
10920     /**
10921     * Adds elements to this composite.
10922     * @param {String/Array} els A string CSS selector, an array of elements or an element
10923     * @return {CompositeElement} this
10924     */
10925     add : function(els){
10926         if(typeof els == "string"){
10927             this.addElements(Roo.Element.selectorFunction(els));
10928         }else if(els.length !== undefined){
10929             this.addElements(els);
10930         }else{
10931             this.addElements([els]);
10932         }
10933         return this;
10934     },
10935     /**
10936     * Calls the passed function passing (el, this, index) for each element in this composite.
10937     * @param {Function} fn The function to call
10938     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10939     * @return {CompositeElement} this
10940     */
10941     each : function(fn, scope){
10942         var els = this.elements;
10943         for(var i = 0, len = els.length; i < len; i++){
10944             if(fn.call(scope || els[i], els[i], this, i) === false) {
10945                 break;
10946             }
10947         }
10948         return this;
10949     },
10950
10951     /**
10952      * Returns the Element object at the specified index
10953      * @param {Number} index
10954      * @return {Roo.Element}
10955      */
10956     item : function(index){
10957         return this.elements[index] || null;
10958     },
10959
10960     /**
10961      * Returns the first Element
10962      * @return {Roo.Element}
10963      */
10964     first : function(){
10965         return this.item(0);
10966     },
10967
10968     /**
10969      * Returns the last Element
10970      * @return {Roo.Element}
10971      */
10972     last : function(){
10973         return this.item(this.elements.length-1);
10974     },
10975
10976     /**
10977      * Returns the number of elements in this composite
10978      * @return Number
10979      */
10980     getCount : function(){
10981         return this.elements.length;
10982     },
10983
10984     /**
10985      * Returns true if this composite contains the passed element
10986      * @return Boolean
10987      */
10988     contains : function(el){
10989         return this.indexOf(el) !== -1;
10990     },
10991
10992     /**
10993      * Returns true if this composite contains the passed element
10994      * @return Boolean
10995      */
10996     indexOf : function(el){
10997         return this.elements.indexOf(Roo.get(el));
10998     },
10999
11000
11001     /**
11002     * Removes the specified element(s).
11003     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11004     * or an array of any of those.
11005     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11006     * @return {CompositeElement} this
11007     */
11008     removeElement : function(el, removeDom){
11009         if(el instanceof Array){
11010             for(var i = 0, len = el.length; i < len; i++){
11011                 this.removeElement(el[i]);
11012             }
11013             return this;
11014         }
11015         var index = typeof el == 'number' ? el : this.indexOf(el);
11016         if(index !== -1){
11017             if(removeDom){
11018                 var d = this.elements[index];
11019                 if(d.dom){
11020                     d.remove();
11021                 }else{
11022                     d.parentNode.removeChild(d);
11023                 }
11024             }
11025             this.elements.splice(index, 1);
11026         }
11027         return this;
11028     },
11029
11030     /**
11031     * Replaces the specified element with the passed element.
11032     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11033     * to replace.
11034     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11035     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11036     * @return {CompositeElement} this
11037     */
11038     replaceElement : function(el, replacement, domReplace){
11039         var index = typeof el == 'number' ? el : this.indexOf(el);
11040         if(index !== -1){
11041             if(domReplace){
11042                 this.elements[index].replaceWith(replacement);
11043             }else{
11044                 this.elements.splice(index, 1, Roo.get(replacement))
11045             }
11046         }
11047         return this;
11048     },
11049
11050     /**
11051      * Removes all elements.
11052      */
11053     clear : function(){
11054         this.elements = [];
11055     }
11056 };
11057 (function(){
11058     Roo.CompositeElement.createCall = function(proto, fnName){
11059         if(!proto[fnName]){
11060             proto[fnName] = function(){
11061                 return this.invoke(fnName, arguments);
11062             };
11063         }
11064     };
11065     for(var fnName in Roo.Element.prototype){
11066         if(typeof Roo.Element.prototype[fnName] == "function"){
11067             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11068         }
11069     };
11070 })();
11071 /*
11072  * Based on:
11073  * Ext JS Library 1.1.1
11074  * Copyright(c) 2006-2007, Ext JS, LLC.
11075  *
11076  * Originally Released Under LGPL - original licence link has changed is not relivant.
11077  *
11078  * Fork - LGPL
11079  * <script type="text/javascript">
11080  */
11081
11082 /**
11083  * @class Roo.CompositeElementLite
11084  * @extends Roo.CompositeElement
11085  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11086  <pre><code>
11087  var els = Roo.select("#some-el div.some-class");
11088  // or select directly from an existing element
11089  var el = Roo.get('some-el');
11090  el.select('div.some-class');
11091
11092  els.setWidth(100); // all elements become 100 width
11093  els.hide(true); // all elements fade out and hide
11094  // or
11095  els.setWidth(100).hide(true);
11096  </code></pre><br><br>
11097  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11098  * actions will be performed on all the elements in this collection.</b>
11099  */
11100 Roo.CompositeElementLite = function(els){
11101     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11102     this.el = new Roo.Element.Flyweight();
11103 };
11104 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11105     addElements : function(els){
11106         if(els){
11107             if(els instanceof Array){
11108                 this.elements = this.elements.concat(els);
11109             }else{
11110                 var yels = this.elements;
11111                 var index = yels.length-1;
11112                 for(var i = 0, len = els.length; i < len; i++) {
11113                     yels[++index] = els[i];
11114                 }
11115             }
11116         }
11117         return this;
11118     },
11119     invoke : function(fn, args){
11120         var els = this.elements;
11121         var el = this.el;
11122         for(var i = 0, len = els.length; i < len; i++) {
11123             el.dom = els[i];
11124                 Roo.Element.prototype[fn].apply(el, args);
11125         }
11126         return this;
11127     },
11128     /**
11129      * Returns a flyweight Element of the dom element object at the specified index
11130      * @param {Number} index
11131      * @return {Roo.Element}
11132      */
11133     item : function(index){
11134         if(!this.elements[index]){
11135             return null;
11136         }
11137         this.el.dom = this.elements[index];
11138         return this.el;
11139     },
11140
11141     // fixes scope with flyweight
11142     addListener : function(eventName, handler, scope, opt){
11143         var els = this.elements;
11144         for(var i = 0, len = els.length; i < len; i++) {
11145             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11146         }
11147         return this;
11148     },
11149
11150     /**
11151     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11152     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11153     * a reference to the dom node, use el.dom.</b>
11154     * @param {Function} fn The function to call
11155     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11156     * @return {CompositeElement} this
11157     */
11158     each : function(fn, scope){
11159         var els = this.elements;
11160         var el = this.el;
11161         for(var i = 0, len = els.length; i < len; i++){
11162             el.dom = els[i];
11163                 if(fn.call(scope || el, el, this, i) === false){
11164                 break;
11165             }
11166         }
11167         return this;
11168     },
11169
11170     indexOf : function(el){
11171         return this.elements.indexOf(Roo.getDom(el));
11172     },
11173
11174     replaceElement : function(el, replacement, domReplace){
11175         var index = typeof el == 'number' ? el : this.indexOf(el);
11176         if(index !== -1){
11177             replacement = Roo.getDom(replacement);
11178             if(domReplace){
11179                 var d = this.elements[index];
11180                 d.parentNode.insertBefore(replacement, d);
11181                 d.parentNode.removeChild(d);
11182             }
11183             this.elements.splice(index, 1, replacement);
11184         }
11185         return this;
11186     }
11187 });
11188 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11189
11190 /*
11191  * Based on:
11192  * Ext JS Library 1.1.1
11193  * Copyright(c) 2006-2007, Ext JS, LLC.
11194  *
11195  * Originally Released Under LGPL - original licence link has changed is not relivant.
11196  *
11197  * Fork - LGPL
11198  * <script type="text/javascript">
11199  */
11200
11201  
11202
11203 /**
11204  * @class Roo.data.Connection
11205  * @extends Roo.util.Observable
11206  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11207  * either to a configured URL, or to a URL specified at request time.<br><br>
11208  * <p>
11209  * Requests made by this class are asynchronous, and will return immediately. No data from
11210  * the server will be available to the statement immediately following the {@link #request} call.
11211  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11212  * <p>
11213  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11214  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11215  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11216  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11217  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11218  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11219  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11220  * standard DOM methods.
11221  * @constructor
11222  * @param {Object} config a configuration object.
11223  */
11224 Roo.data.Connection = function(config){
11225     Roo.apply(this, config);
11226     this.addEvents({
11227         /**
11228          * @event beforerequest
11229          * Fires before a network request is made to retrieve a data object.
11230          * @param {Connection} conn This Connection object.
11231          * @param {Object} options The options config object passed to the {@link #request} method.
11232          */
11233         "beforerequest" : true,
11234         /**
11235          * @event requestcomplete
11236          * Fires if the request was successfully completed.
11237          * @param {Connection} conn This Connection object.
11238          * @param {Object} response The XHR object containing the response data.
11239          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11240          * @param {Object} options The options config object passed to the {@link #request} method.
11241          */
11242         "requestcomplete" : true,
11243         /**
11244          * @event requestexception
11245          * Fires if an error HTTP status was returned from the server.
11246          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11247          * @param {Connection} conn This Connection object.
11248          * @param {Object} response The XHR object containing the response data.
11249          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11250          * @param {Object} options The options config object passed to the {@link #request} method.
11251          */
11252         "requestexception" : true
11253     });
11254     Roo.data.Connection.superclass.constructor.call(this);
11255 };
11256
11257 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11258     /**
11259      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11260      */
11261     /**
11262      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11263      * extra parameters to each request made by this object. (defaults to undefined)
11264      */
11265     /**
11266      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11267      *  to each request made by this object. (defaults to undefined)
11268      */
11269     /**
11270      * @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)
11271      */
11272     /**
11273      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11274      */
11275     timeout : 30000,
11276     /**
11277      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11278      * @type Boolean
11279      */
11280     autoAbort:false,
11281
11282     /**
11283      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11284      * @type Boolean
11285      */
11286     disableCaching: true,
11287
11288     /**
11289      * Sends an HTTP request to a remote server.
11290      * @param {Object} options An object which may contain the following properties:<ul>
11291      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11292      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11293      * request, a url encoded string or a function to call to get either.</li>
11294      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11295      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11296      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11297      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11298      * <li>options {Object} The parameter to the request call.</li>
11299      * <li>success {Boolean} True if the request succeeded.</li>
11300      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11301      * </ul></li>
11302      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11303      * The callback is passed the following parameters:<ul>
11304      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11305      * <li>options {Object} The parameter to the request call.</li>
11306      * </ul></li>
11307      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11308      * The callback is passed the following parameters:<ul>
11309      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11310      * <li>options {Object} The parameter to the request call.</li>
11311      * </ul></li>
11312      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11313      * for the callback function. Defaults to the browser window.</li>
11314      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11315      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11316      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11317      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11318      * params for the post data. Any params will be appended to the URL.</li>
11319      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11320      * </ul>
11321      * @return {Number} transactionId
11322      */
11323     request : function(o){
11324         if(this.fireEvent("beforerequest", this, o) !== false){
11325             var p = o.params;
11326
11327             if(typeof p == "function"){
11328                 p = p.call(o.scope||window, o);
11329             }
11330             if(typeof p == "object"){
11331                 p = Roo.urlEncode(o.params);
11332             }
11333             if(this.extraParams){
11334                 var extras = Roo.urlEncode(this.extraParams);
11335                 p = p ? (p + '&' + extras) : extras;
11336             }
11337
11338             var url = o.url || this.url;
11339             if(typeof url == 'function'){
11340                 url = url.call(o.scope||window, o);
11341             }
11342
11343             if(o.form){
11344                 var form = Roo.getDom(o.form);
11345                 url = url || form.action;
11346
11347                 var enctype = form.getAttribute("enctype");
11348                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11349                     return this.doFormUpload(o, p, url);
11350                 }
11351                 var f = Roo.lib.Ajax.serializeForm(form);
11352                 p = p ? (p + '&' + f) : f;
11353             }
11354
11355             var hs = o.headers;
11356             if(this.defaultHeaders){
11357                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11358                 if(!o.headers){
11359                     o.headers = hs;
11360                 }
11361             }
11362
11363             var cb = {
11364                 success: this.handleResponse,
11365                 failure: this.handleFailure,
11366                 scope: this,
11367                 argument: {options: o},
11368                 timeout : this.timeout
11369             };
11370
11371             var method = o.method||this.method||(p ? "POST" : "GET");
11372
11373             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11374                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11375             }
11376
11377             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11378                 if(o.autoAbort){
11379                     this.abort();
11380                 }
11381             }else if(this.autoAbort !== false){
11382                 this.abort();
11383             }
11384
11385             if((method == 'GET' && p) || o.xmlData){
11386                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11387                 p = '';
11388             }
11389             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11390             return this.transId;
11391         }else{
11392             Roo.callback(o.callback, o.scope, [o, null, null]);
11393             return null;
11394         }
11395     },
11396
11397     /**
11398      * Determine whether this object has a request outstanding.
11399      * @param {Number} transactionId (Optional) defaults to the last transaction
11400      * @return {Boolean} True if there is an outstanding request.
11401      */
11402     isLoading : function(transId){
11403         if(transId){
11404             return Roo.lib.Ajax.isCallInProgress(transId);
11405         }else{
11406             return this.transId ? true : false;
11407         }
11408     },
11409
11410     /**
11411      * Aborts any outstanding request.
11412      * @param {Number} transactionId (Optional) defaults to the last transaction
11413      */
11414     abort : function(transId){
11415         if(transId || this.isLoading()){
11416             Roo.lib.Ajax.abort(transId || this.transId);
11417         }
11418     },
11419
11420     // private
11421     handleResponse : function(response){
11422         this.transId = false;
11423         var options = response.argument.options;
11424         response.argument = options ? options.argument : null;
11425         this.fireEvent("requestcomplete", this, response, options);
11426         Roo.callback(options.success, options.scope, [response, options]);
11427         Roo.callback(options.callback, options.scope, [options, true, response]);
11428     },
11429
11430     // private
11431     handleFailure : function(response, e){
11432         this.transId = false;
11433         var options = response.argument.options;
11434         response.argument = options ? options.argument : null;
11435         this.fireEvent("requestexception", this, response, options, e);
11436         Roo.callback(options.failure, options.scope, [response, options]);
11437         Roo.callback(options.callback, options.scope, [options, false, response]);
11438     },
11439
11440     // private
11441     doFormUpload : function(o, ps, url){
11442         var id = Roo.id();
11443         var frame = document.createElement('iframe');
11444         frame.id = id;
11445         frame.name = id;
11446         frame.className = 'x-hidden';
11447         if(Roo.isIE){
11448             frame.src = Roo.SSL_SECURE_URL;
11449         }
11450         document.body.appendChild(frame);
11451
11452         if(Roo.isIE){
11453            document.frames[id].name = id;
11454         }
11455
11456         var form = Roo.getDom(o.form);
11457         form.target = id;
11458         form.method = 'POST';
11459         form.enctype = form.encoding = 'multipart/form-data';
11460         if(url){
11461             form.action = url;
11462         }
11463
11464         var hiddens, hd;
11465         if(ps){ // add dynamic params
11466             hiddens = [];
11467             ps = Roo.urlDecode(ps, false);
11468             for(var k in ps){
11469                 if(ps.hasOwnProperty(k)){
11470                     hd = document.createElement('input');
11471                     hd.type = 'hidden';
11472                     hd.name = k;
11473                     hd.value = ps[k];
11474                     form.appendChild(hd);
11475                     hiddens.push(hd);
11476                 }
11477             }
11478         }
11479
11480         function cb(){
11481             var r = {  // bogus response object
11482                 responseText : '',
11483                 responseXML : null
11484             };
11485
11486             r.argument = o ? o.argument : null;
11487
11488             try { //
11489                 var doc;
11490                 if(Roo.isIE){
11491                     doc = frame.contentWindow.document;
11492                 }else {
11493                     doc = (frame.contentDocument || window.frames[id].document);
11494                 }
11495                 if(doc && doc.body){
11496                     r.responseText = doc.body.innerHTML;
11497                 }
11498                 if(doc && doc.XMLDocument){
11499                     r.responseXML = doc.XMLDocument;
11500                 }else {
11501                     r.responseXML = doc;
11502                 }
11503             }
11504             catch(e) {
11505                 // ignore
11506             }
11507
11508             Roo.EventManager.removeListener(frame, 'load', cb, this);
11509
11510             this.fireEvent("requestcomplete", this, r, o);
11511             Roo.callback(o.success, o.scope, [r, o]);
11512             Roo.callback(o.callback, o.scope, [o, true, r]);
11513
11514             setTimeout(function(){document.body.removeChild(frame);}, 100);
11515         }
11516
11517         Roo.EventManager.on(frame, 'load', cb, this);
11518         form.submit();
11519
11520         if(hiddens){ // remove dynamic params
11521             for(var i = 0, len = hiddens.length; i < len; i++){
11522                 form.removeChild(hiddens[i]);
11523             }
11524         }
11525     }
11526 });
11527
11528 /**
11529  * @class Roo.Ajax
11530  * @extends Roo.data.Connection
11531  * Global Ajax request class.
11532  *
11533  * @singleton
11534  */
11535 Roo.Ajax = new Roo.data.Connection({
11536     // fix up the docs
11537    /**
11538      * @cfg {String} url @hide
11539      */
11540     /**
11541      * @cfg {Object} extraParams @hide
11542      */
11543     /**
11544      * @cfg {Object} defaultHeaders @hide
11545      */
11546     /**
11547      * @cfg {String} method (Optional) @hide
11548      */
11549     /**
11550      * @cfg {Number} timeout (Optional) @hide
11551      */
11552     /**
11553      * @cfg {Boolean} autoAbort (Optional) @hide
11554      */
11555
11556     /**
11557      * @cfg {Boolean} disableCaching (Optional) @hide
11558      */
11559
11560     /**
11561      * @property  disableCaching
11562      * True to add a unique cache-buster param to GET requests. (defaults to true)
11563      * @type Boolean
11564      */
11565     /**
11566      * @property  url
11567      * The default URL to be used for requests to the server. (defaults to undefined)
11568      * @type String
11569      */
11570     /**
11571      * @property  extraParams
11572      * An object containing properties which are used as
11573      * extra parameters to each request made by this object. (defaults to undefined)
11574      * @type Object
11575      */
11576     /**
11577      * @property  defaultHeaders
11578      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11579      * @type Object
11580      */
11581     /**
11582      * @property  method
11583      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11584      * @type String
11585      */
11586     /**
11587      * @property  timeout
11588      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11589      * @type Number
11590      */
11591
11592     /**
11593      * @property  autoAbort
11594      * Whether a new request should abort any pending requests. (defaults to false)
11595      * @type Boolean
11596      */
11597     autoAbort : false,
11598
11599     /**
11600      * Serialize the passed form into a url encoded string
11601      * @param {String/HTMLElement} form
11602      * @return {String}
11603      */
11604     serializeForm : function(form){
11605         return Roo.lib.Ajax.serializeForm(form);
11606     }
11607 });/*
11608  * Based on:
11609  * Ext JS Library 1.1.1
11610  * Copyright(c) 2006-2007, Ext JS, LLC.
11611  *
11612  * Originally Released Under LGPL - original licence link has changed is not relivant.
11613  *
11614  * Fork - LGPL
11615  * <script type="text/javascript">
11616  */
11617  
11618 /**
11619  * Global Ajax request class.
11620  * 
11621  * @class Roo.Ajax
11622  * @extends Roo.data.Connection
11623  * @static
11624  * 
11625  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11626  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11627  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11628  * @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)
11629  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11630  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11631  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11632  */
11633 Roo.Ajax = new Roo.data.Connection({
11634     // fix up the docs
11635     /**
11636      * @scope Roo.Ajax
11637      * @type {Boolear} 
11638      */
11639     autoAbort : false,
11640
11641     /**
11642      * Serialize the passed form into a url encoded string
11643      * @scope Roo.Ajax
11644      * @param {String/HTMLElement} form
11645      * @return {String}
11646      */
11647     serializeForm : function(form){
11648         return Roo.lib.Ajax.serializeForm(form);
11649     }
11650 });/*
11651  * Based on:
11652  * Ext JS Library 1.1.1
11653  * Copyright(c) 2006-2007, Ext JS, LLC.
11654  *
11655  * Originally Released Under LGPL - original licence link has changed is not relivant.
11656  *
11657  * Fork - LGPL
11658  * <script type="text/javascript">
11659  */
11660
11661  
11662 /**
11663  * @class Roo.UpdateManager
11664  * @extends Roo.util.Observable
11665  * Provides AJAX-style update for Element object.<br><br>
11666  * Usage:<br>
11667  * <pre><code>
11668  * // Get it from a Roo.Element object
11669  * var el = Roo.get("foo");
11670  * var mgr = el.getUpdateManager();
11671  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11672  * ...
11673  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11674  * <br>
11675  * // or directly (returns the same UpdateManager instance)
11676  * var mgr = new Roo.UpdateManager("myElementId");
11677  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11678  * mgr.on("update", myFcnNeedsToKnow);
11679  * <br>
11680    // short handed call directly from the element object
11681    Roo.get("foo").load({
11682         url: "bar.php",
11683         scripts:true,
11684         params: "for=bar",
11685         text: "Loading Foo..."
11686    });
11687  * </code></pre>
11688  * @constructor
11689  * Create new UpdateManager directly.
11690  * @param {String/HTMLElement/Roo.Element} el The element to update
11691  * @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).
11692  */
11693 Roo.UpdateManager = function(el, forceNew){
11694     el = Roo.get(el);
11695     if(!forceNew && el.updateManager){
11696         return el.updateManager;
11697     }
11698     /**
11699      * The Element object
11700      * @type Roo.Element
11701      */
11702     this.el = el;
11703     /**
11704      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11705      * @type String
11706      */
11707     this.defaultUrl = null;
11708
11709     this.addEvents({
11710         /**
11711          * @event beforeupdate
11712          * Fired before an update is made, return false from your handler and the update is cancelled.
11713          * @param {Roo.Element} el
11714          * @param {String/Object/Function} url
11715          * @param {String/Object} params
11716          */
11717         "beforeupdate": true,
11718         /**
11719          * @event update
11720          * Fired after successful update is made.
11721          * @param {Roo.Element} el
11722          * @param {Object} oResponseObject The response Object
11723          */
11724         "update": true,
11725         /**
11726          * @event failure
11727          * Fired on update failure.
11728          * @param {Roo.Element} el
11729          * @param {Object} oResponseObject The response Object
11730          */
11731         "failure": true
11732     });
11733     var d = Roo.UpdateManager.defaults;
11734     /**
11735      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11736      * @type String
11737      */
11738     this.sslBlankUrl = d.sslBlankUrl;
11739     /**
11740      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11741      * @type Boolean
11742      */
11743     this.disableCaching = d.disableCaching;
11744     /**
11745      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11746      * @type String
11747      */
11748     this.indicatorText = d.indicatorText;
11749     /**
11750      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11751      * @type String
11752      */
11753     this.showLoadIndicator = d.showLoadIndicator;
11754     /**
11755      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11756      * @type Number
11757      */
11758     this.timeout = d.timeout;
11759
11760     /**
11761      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11762      * @type Boolean
11763      */
11764     this.loadScripts = d.loadScripts;
11765
11766     /**
11767      * Transaction object of current executing transaction
11768      */
11769     this.transaction = null;
11770
11771     /**
11772      * @private
11773      */
11774     this.autoRefreshProcId = null;
11775     /**
11776      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11777      * @type Function
11778      */
11779     this.refreshDelegate = this.refresh.createDelegate(this);
11780     /**
11781      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11782      * @type Function
11783      */
11784     this.updateDelegate = this.update.createDelegate(this);
11785     /**
11786      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11787      * @type Function
11788      */
11789     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11790     /**
11791      * @private
11792      */
11793     this.successDelegate = this.processSuccess.createDelegate(this);
11794     /**
11795      * @private
11796      */
11797     this.failureDelegate = this.processFailure.createDelegate(this);
11798
11799     if(!this.renderer){
11800      /**
11801       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11802       */
11803     this.renderer = new Roo.UpdateManager.BasicRenderer();
11804     }
11805     
11806     Roo.UpdateManager.superclass.constructor.call(this);
11807 };
11808
11809 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11810     /**
11811      * Get the Element this UpdateManager is bound to
11812      * @return {Roo.Element} The element
11813      */
11814     getEl : function(){
11815         return this.el;
11816     },
11817     /**
11818      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11819      * @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:
11820 <pre><code>
11821 um.update({<br/>
11822     url: "your-url.php",<br/>
11823     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11824     callback: yourFunction,<br/>
11825     scope: yourObject, //(optional scope)  <br/>
11826     discardUrl: false, <br/>
11827     nocache: false,<br/>
11828     text: "Loading...",<br/>
11829     timeout: 30,<br/>
11830     scripts: false<br/>
11831 });
11832 </code></pre>
11833      * The only required property is url. The optional properties nocache, text and scripts
11834      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11835      * @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}
11836      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11837      * @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.
11838      */
11839     update : function(url, params, callback, discardUrl){
11840         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11841             var method = this.method, cfg;
11842             if(typeof url == "object"){ // must be config object
11843                 cfg = url;
11844                 url = cfg.url;
11845                 params = params || cfg.params;
11846                 callback = callback || cfg.callback;
11847                 discardUrl = discardUrl || cfg.discardUrl;
11848                 if(callback && cfg.scope){
11849                     callback = callback.createDelegate(cfg.scope);
11850                 }
11851                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11852                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11853                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11854                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11855                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11856             }
11857             this.showLoading();
11858             if(!discardUrl){
11859                 this.defaultUrl = url;
11860             }
11861             if(typeof url == "function"){
11862                 url = url.call(this);
11863             }
11864
11865             method = method || (params ? "POST" : "GET");
11866             if(method == "GET"){
11867                 url = this.prepareUrl(url);
11868             }
11869
11870             var o = Roo.apply(cfg ||{}, {
11871                 url : url,
11872                 params: params,
11873                 success: this.successDelegate,
11874                 failure: this.failureDelegate,
11875                 callback: undefined,
11876                 timeout: (this.timeout*1000),
11877                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11878             });
11879
11880             this.transaction = Roo.Ajax.request(o);
11881         }
11882     },
11883
11884     /**
11885      * 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.
11886      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11887      * @param {String/HTMLElement} form The form Id or form element
11888      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11889      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11890      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11891      */
11892     formUpdate : function(form, url, reset, callback){
11893         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11894             if(typeof url == "function"){
11895                 url = url.call(this);
11896             }
11897             form = Roo.getDom(form);
11898             this.transaction = Roo.Ajax.request({
11899                 form: form,
11900                 url:url,
11901                 success: this.successDelegate,
11902                 failure: this.failureDelegate,
11903                 timeout: (this.timeout*1000),
11904                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11905             });
11906             this.showLoading.defer(1, this);
11907         }
11908     },
11909
11910     /**
11911      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11912      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11913      */
11914     refresh : function(callback){
11915         if(this.defaultUrl == null){
11916             return;
11917         }
11918         this.update(this.defaultUrl, null, callback, true);
11919     },
11920
11921     /**
11922      * Set this element to auto refresh.
11923      * @param {Number} interval How often to update (in seconds).
11924      * @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)
11925      * @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}
11926      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11927      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11928      */
11929     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11930         if(refreshNow){
11931             this.update(url || this.defaultUrl, params, callback, true);
11932         }
11933         if(this.autoRefreshProcId){
11934             clearInterval(this.autoRefreshProcId);
11935         }
11936         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11937     },
11938
11939     /**
11940      * Stop auto refresh on this element.
11941      */
11942      stopAutoRefresh : function(){
11943         if(this.autoRefreshProcId){
11944             clearInterval(this.autoRefreshProcId);
11945             delete this.autoRefreshProcId;
11946         }
11947     },
11948
11949     isAutoRefreshing : function(){
11950        return this.autoRefreshProcId ? true : false;
11951     },
11952     /**
11953      * Called to update the element to "Loading" state. Override to perform custom action.
11954      */
11955     showLoading : function(){
11956         if(this.showLoadIndicator){
11957             this.el.update(this.indicatorText);
11958         }
11959     },
11960
11961     /**
11962      * Adds unique parameter to query string if disableCaching = true
11963      * @private
11964      */
11965     prepareUrl : function(url){
11966         if(this.disableCaching){
11967             var append = "_dc=" + (new Date().getTime());
11968             if(url.indexOf("?") !== -1){
11969                 url += "&" + append;
11970             }else{
11971                 url += "?" + append;
11972             }
11973         }
11974         return url;
11975     },
11976
11977     /**
11978      * @private
11979      */
11980     processSuccess : function(response){
11981         this.transaction = null;
11982         if(response.argument.form && response.argument.reset){
11983             try{ // put in try/catch since some older FF releases had problems with this
11984                 response.argument.form.reset();
11985             }catch(e){}
11986         }
11987         if(this.loadScripts){
11988             this.renderer.render(this.el, response, this,
11989                 this.updateComplete.createDelegate(this, [response]));
11990         }else{
11991             this.renderer.render(this.el, response, this);
11992             this.updateComplete(response);
11993         }
11994     },
11995
11996     updateComplete : function(response){
11997         this.fireEvent("update", this.el, response);
11998         if(typeof response.argument.callback == "function"){
11999             response.argument.callback(this.el, true, response);
12000         }
12001     },
12002
12003     /**
12004      * @private
12005      */
12006     processFailure : function(response){
12007         this.transaction = null;
12008         this.fireEvent("failure", this.el, response);
12009         if(typeof response.argument.callback == "function"){
12010             response.argument.callback(this.el, false, response);
12011         }
12012     },
12013
12014     /**
12015      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12016      * @param {Object} renderer The object implementing the render() method
12017      */
12018     setRenderer : function(renderer){
12019         this.renderer = renderer;
12020     },
12021
12022     getRenderer : function(){
12023        return this.renderer;
12024     },
12025
12026     /**
12027      * Set the defaultUrl used for updates
12028      * @param {String/Function} defaultUrl The url or a function to call to get the url
12029      */
12030     setDefaultUrl : function(defaultUrl){
12031         this.defaultUrl = defaultUrl;
12032     },
12033
12034     /**
12035      * Aborts the executing transaction
12036      */
12037     abort : function(){
12038         if(this.transaction){
12039             Roo.Ajax.abort(this.transaction);
12040         }
12041     },
12042
12043     /**
12044      * Returns true if an update is in progress
12045      * @return {Boolean}
12046      */
12047     isUpdating : function(){
12048         if(this.transaction){
12049             return Roo.Ajax.isLoading(this.transaction);
12050         }
12051         return false;
12052     }
12053 });
12054
12055 /**
12056  * @class Roo.UpdateManager.defaults
12057  * @static (not really - but it helps the doc tool)
12058  * The defaults collection enables customizing the default properties of UpdateManager
12059  */
12060    Roo.UpdateManager.defaults = {
12061        /**
12062          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12063          * @type Number
12064          */
12065          timeout : 30,
12066
12067          /**
12068          * True to process scripts by default (Defaults to false).
12069          * @type Boolean
12070          */
12071         loadScripts : false,
12072
12073         /**
12074         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12075         * @type String
12076         */
12077         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12078         /**
12079          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12080          * @type Boolean
12081          */
12082         disableCaching : false,
12083         /**
12084          * Whether to show indicatorText when loading (Defaults to true).
12085          * @type Boolean
12086          */
12087         showLoadIndicator : true,
12088         /**
12089          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12090          * @type String
12091          */
12092         indicatorText : '<div class="loading-indicator">Loading...</div>'
12093    };
12094
12095 /**
12096  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12097  *Usage:
12098  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12099  * @param {String/HTMLElement/Roo.Element} el The element to update
12100  * @param {String} url The url
12101  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12102  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12103  * @static
12104  * @deprecated
12105  * @member Roo.UpdateManager
12106  */
12107 Roo.UpdateManager.updateElement = function(el, url, params, options){
12108     var um = Roo.get(el, true).getUpdateManager();
12109     Roo.apply(um, options);
12110     um.update(url, params, options ? options.callback : null);
12111 };
12112 // alias for backwards compat
12113 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12114 /**
12115  * @class Roo.UpdateManager.BasicRenderer
12116  * Default Content renderer. Updates the elements innerHTML with the responseText.
12117  */
12118 Roo.UpdateManager.BasicRenderer = function(){};
12119
12120 Roo.UpdateManager.BasicRenderer.prototype = {
12121     /**
12122      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12123      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12124      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12125      * @param {Roo.Element} el The element being rendered
12126      * @param {Object} response The YUI Connect response object
12127      * @param {UpdateManager} updateManager The calling update manager
12128      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12129      */
12130      render : function(el, response, updateManager, callback){
12131         el.update(response.responseText, updateManager.loadScripts, callback);
12132     }
12133 };
12134 /*
12135  * Based on:
12136  * Ext JS Library 1.1.1
12137  * Copyright(c) 2006-2007, Ext JS, LLC.
12138  *
12139  * Originally Released Under LGPL - original licence link has changed is not relivant.
12140  *
12141  * Fork - LGPL
12142  * <script type="text/javascript">
12143  */
12144
12145 /**
12146  * @class Roo.util.DelayedTask
12147  * Provides a convenient method of performing setTimeout where a new
12148  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12149  * You can use this class to buffer
12150  * the keypress events for a certain number of milliseconds, and perform only if they stop
12151  * for that amount of time.
12152  * @constructor The parameters to this constructor serve as defaults and are not required.
12153  * @param {Function} fn (optional) The default function to timeout
12154  * @param {Object} scope (optional) The default scope of that timeout
12155  * @param {Array} args (optional) The default Array of arguments
12156  */
12157 Roo.util.DelayedTask = function(fn, scope, args){
12158     var id = null, d, t;
12159
12160     var call = function(){
12161         var now = new Date().getTime();
12162         if(now - t >= d){
12163             clearInterval(id);
12164             id = null;
12165             fn.apply(scope, args || []);
12166         }
12167     };
12168     /**
12169      * Cancels any pending timeout and queues a new one
12170      * @param {Number} delay The milliseconds to delay
12171      * @param {Function} newFn (optional) Overrides function passed to constructor
12172      * @param {Object} newScope (optional) Overrides scope passed to constructor
12173      * @param {Array} newArgs (optional) Overrides args passed to constructor
12174      */
12175     this.delay = function(delay, newFn, newScope, newArgs){
12176         if(id && delay != d){
12177             this.cancel();
12178         }
12179         d = delay;
12180         t = new Date().getTime();
12181         fn = newFn || fn;
12182         scope = newScope || scope;
12183         args = newArgs || args;
12184         if(!id){
12185             id = setInterval(call, d);
12186         }
12187     };
12188
12189     /**
12190      * Cancel the last queued timeout
12191      */
12192     this.cancel = function(){
12193         if(id){
12194             clearInterval(id);
12195             id = null;
12196         }
12197     };
12198 };/*
12199  * Based on:
12200  * Ext JS Library 1.1.1
12201  * Copyright(c) 2006-2007, Ext JS, LLC.
12202  *
12203  * Originally Released Under LGPL - original licence link has changed is not relivant.
12204  *
12205  * Fork - LGPL
12206  * <script type="text/javascript">
12207  */
12208  
12209  
12210 Roo.util.TaskRunner = function(interval){
12211     interval = interval || 10;
12212     var tasks = [], removeQueue = [];
12213     var id = 0;
12214     var running = false;
12215
12216     var stopThread = function(){
12217         running = false;
12218         clearInterval(id);
12219         id = 0;
12220     };
12221
12222     var startThread = function(){
12223         if(!running){
12224             running = true;
12225             id = setInterval(runTasks, interval);
12226         }
12227     };
12228
12229     var removeTask = function(task){
12230         removeQueue.push(task);
12231         if(task.onStop){
12232             task.onStop();
12233         }
12234     };
12235
12236     var runTasks = function(){
12237         if(removeQueue.length > 0){
12238             for(var i = 0, len = removeQueue.length; i < len; i++){
12239                 tasks.remove(removeQueue[i]);
12240             }
12241             removeQueue = [];
12242             if(tasks.length < 1){
12243                 stopThread();
12244                 return;
12245             }
12246         }
12247         var now = new Date().getTime();
12248         for(var i = 0, len = tasks.length; i < len; ++i){
12249             var t = tasks[i];
12250             var itime = now - t.taskRunTime;
12251             if(t.interval <= itime){
12252                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12253                 t.taskRunTime = now;
12254                 if(rt === false || t.taskRunCount === t.repeat){
12255                     removeTask(t);
12256                     return;
12257                 }
12258             }
12259             if(t.duration && t.duration <= (now - t.taskStartTime)){
12260                 removeTask(t);
12261             }
12262         }
12263     };
12264
12265     /**
12266      * Queues a new task.
12267      * @param {Object} task
12268      */
12269     this.start = function(task){
12270         tasks.push(task);
12271         task.taskStartTime = new Date().getTime();
12272         task.taskRunTime = 0;
12273         task.taskRunCount = 0;
12274         startThread();
12275         return task;
12276     };
12277
12278     this.stop = function(task){
12279         removeTask(task);
12280         return task;
12281     };
12282
12283     this.stopAll = function(){
12284         stopThread();
12285         for(var i = 0, len = tasks.length; i < len; i++){
12286             if(tasks[i].onStop){
12287                 tasks[i].onStop();
12288             }
12289         }
12290         tasks = [];
12291         removeQueue = [];
12292     };
12293 };
12294
12295 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12296  * Based on:
12297  * Ext JS Library 1.1.1
12298  * Copyright(c) 2006-2007, Ext JS, LLC.
12299  *
12300  * Originally Released Under LGPL - original licence link has changed is not relivant.
12301  *
12302  * Fork - LGPL
12303  * <script type="text/javascript">
12304  */
12305
12306  
12307 /**
12308  * @class Roo.util.MixedCollection
12309  * @extends Roo.util.Observable
12310  * A Collection class that maintains both numeric indexes and keys and exposes events.
12311  * @constructor
12312  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12313  * collection (defaults to false)
12314  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12315  * and return the key value for that item.  This is used when available to look up the key on items that
12316  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12317  * equivalent to providing an implementation for the {@link #getKey} method.
12318  */
12319 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12320     this.items = [];
12321     this.map = {};
12322     this.keys = [];
12323     this.length = 0;
12324     this.addEvents({
12325         /**
12326          * @event clear
12327          * Fires when the collection is cleared.
12328          */
12329         "clear" : true,
12330         /**
12331          * @event add
12332          * Fires when an item is added to the collection.
12333          * @param {Number} index The index at which the item was added.
12334          * @param {Object} o The item added.
12335          * @param {String} key The key associated with the added item.
12336          */
12337         "add" : true,
12338         /**
12339          * @event replace
12340          * Fires when an item is replaced in the collection.
12341          * @param {String} key he key associated with the new added.
12342          * @param {Object} old The item being replaced.
12343          * @param {Object} new The new item.
12344          */
12345         "replace" : true,
12346         /**
12347          * @event remove
12348          * Fires when an item is removed from the collection.
12349          * @param {Object} o The item being removed.
12350          * @param {String} key (optional) The key associated with the removed item.
12351          */
12352         "remove" : true,
12353         "sort" : true
12354     });
12355     this.allowFunctions = allowFunctions === true;
12356     if(keyFn){
12357         this.getKey = keyFn;
12358     }
12359     Roo.util.MixedCollection.superclass.constructor.call(this);
12360 };
12361
12362 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12363     allowFunctions : false,
12364     
12365 /**
12366  * Adds an item to the collection.
12367  * @param {String} key The key to associate with the item
12368  * @param {Object} o The item to add.
12369  * @return {Object} The item added.
12370  */
12371     add : function(key, o){
12372         if(arguments.length == 1){
12373             o = arguments[0];
12374             key = this.getKey(o);
12375         }
12376         if(typeof key == "undefined" || key === null){
12377             this.length++;
12378             this.items.push(o);
12379             this.keys.push(null);
12380         }else{
12381             var old = this.map[key];
12382             if(old){
12383                 return this.replace(key, o);
12384             }
12385             this.length++;
12386             this.items.push(o);
12387             this.map[key] = o;
12388             this.keys.push(key);
12389         }
12390         this.fireEvent("add", this.length-1, o, key);
12391         return o;
12392     },
12393        
12394 /**
12395   * MixedCollection has a generic way to fetch keys if you implement getKey.
12396 <pre><code>
12397 // normal way
12398 var mc = new Roo.util.MixedCollection();
12399 mc.add(someEl.dom.id, someEl);
12400 mc.add(otherEl.dom.id, otherEl);
12401 //and so on
12402
12403 // using getKey
12404 var mc = new Roo.util.MixedCollection();
12405 mc.getKey = function(el){
12406    return el.dom.id;
12407 };
12408 mc.add(someEl);
12409 mc.add(otherEl);
12410
12411 // or via the constructor
12412 var mc = new Roo.util.MixedCollection(false, function(el){
12413    return el.dom.id;
12414 });
12415 mc.add(someEl);
12416 mc.add(otherEl);
12417 </code></pre>
12418  * @param o {Object} The item for which to find the key.
12419  * @return {Object} The key for the passed item.
12420  */
12421     getKey : function(o){
12422          return o.id; 
12423     },
12424    
12425 /**
12426  * Replaces an item in the collection.
12427  * @param {String} key The key associated with the item to replace, or the item to replace.
12428  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12429  * @return {Object}  The new item.
12430  */
12431     replace : function(key, o){
12432         if(arguments.length == 1){
12433             o = arguments[0];
12434             key = this.getKey(o);
12435         }
12436         var old = this.item(key);
12437         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12438              return this.add(key, o);
12439         }
12440         var index = this.indexOfKey(key);
12441         this.items[index] = o;
12442         this.map[key] = o;
12443         this.fireEvent("replace", key, old, o);
12444         return o;
12445     },
12446    
12447 /**
12448  * Adds all elements of an Array or an Object to the collection.
12449  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12450  * an Array of values, each of which are added to the collection.
12451  */
12452     addAll : function(objs){
12453         if(arguments.length > 1 || objs instanceof Array){
12454             var args = arguments.length > 1 ? arguments : objs;
12455             for(var i = 0, len = args.length; i < len; i++){
12456                 this.add(args[i]);
12457             }
12458         }else{
12459             for(var key in objs){
12460                 if(this.allowFunctions || typeof objs[key] != "function"){
12461                     this.add(key, objs[key]);
12462                 }
12463             }
12464         }
12465     },
12466    
12467 /**
12468  * Executes the specified function once for every item in the collection, passing each
12469  * item as the first and only parameter. returning false from the function will stop the iteration.
12470  * @param {Function} fn The function to execute for each item.
12471  * @param {Object} scope (optional) The scope in which to execute the function.
12472  */
12473     each : function(fn, scope){
12474         var items = [].concat(this.items); // each safe for removal
12475         for(var i = 0, len = items.length; i < len; i++){
12476             if(fn.call(scope || items[i], items[i], i, len) === false){
12477                 break;
12478             }
12479         }
12480     },
12481    
12482 /**
12483  * Executes the specified function once for every key in the collection, passing each
12484  * key, and its associated item as the first two parameters.
12485  * @param {Function} fn The function to execute for each item.
12486  * @param {Object} scope (optional) The scope in which to execute the function.
12487  */
12488     eachKey : function(fn, scope){
12489         for(var i = 0, len = this.keys.length; i < len; i++){
12490             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12491         }
12492     },
12493    
12494 /**
12495  * Returns the first item in the collection which elicits a true return value from the
12496  * passed selection function.
12497  * @param {Function} fn The selection function to execute for each item.
12498  * @param {Object} scope (optional) The scope in which to execute the function.
12499  * @return {Object} The first item in the collection which returned true from the selection function.
12500  */
12501     find : function(fn, scope){
12502         for(var i = 0, len = this.items.length; i < len; i++){
12503             if(fn.call(scope || window, this.items[i], this.keys[i])){
12504                 return this.items[i];
12505             }
12506         }
12507         return null;
12508     },
12509    
12510 /**
12511  * Inserts an item at the specified index in the collection.
12512  * @param {Number} index The index to insert the item at.
12513  * @param {String} key The key to associate with the new item, or the item itself.
12514  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12515  * @return {Object} The item inserted.
12516  */
12517     insert : function(index, key, o){
12518         if(arguments.length == 2){
12519             o = arguments[1];
12520             key = this.getKey(o);
12521         }
12522         if(index >= this.length){
12523             return this.add(key, o);
12524         }
12525         this.length++;
12526         this.items.splice(index, 0, o);
12527         if(typeof key != "undefined" && key != null){
12528             this.map[key] = o;
12529         }
12530         this.keys.splice(index, 0, key);
12531         this.fireEvent("add", index, o, key);
12532         return o;
12533     },
12534    
12535 /**
12536  * Removed an item from the collection.
12537  * @param {Object} o The item to remove.
12538  * @return {Object} The item removed.
12539  */
12540     remove : function(o){
12541         return this.removeAt(this.indexOf(o));
12542     },
12543    
12544 /**
12545  * Remove an item from a specified index in the collection.
12546  * @param {Number} index The index within the collection of the item to remove.
12547  */
12548     removeAt : function(index){
12549         if(index < this.length && index >= 0){
12550             this.length--;
12551             var o = this.items[index];
12552             this.items.splice(index, 1);
12553             var key = this.keys[index];
12554             if(typeof key != "undefined"){
12555                 delete this.map[key];
12556             }
12557             this.keys.splice(index, 1);
12558             this.fireEvent("remove", o, key);
12559         }
12560     },
12561    
12562 /**
12563  * Removed an item associated with the passed key fom the collection.
12564  * @param {String} key The key of the item to remove.
12565  */
12566     removeKey : function(key){
12567         return this.removeAt(this.indexOfKey(key));
12568     },
12569    
12570 /**
12571  * Returns the number of items in the collection.
12572  * @return {Number} the number of items in the collection.
12573  */
12574     getCount : function(){
12575         return this.length; 
12576     },
12577    
12578 /**
12579  * Returns index within the collection of the passed Object.
12580  * @param {Object} o The item to find the index of.
12581  * @return {Number} index of the item.
12582  */
12583     indexOf : function(o){
12584         if(!this.items.indexOf){
12585             for(var i = 0, len = this.items.length; i < len; i++){
12586                 if(this.items[i] == o) return i;
12587             }
12588             return -1;
12589         }else{
12590             return this.items.indexOf(o);
12591         }
12592     },
12593    
12594 /**
12595  * Returns index within the collection of the passed key.
12596  * @param {String} key The key to find the index of.
12597  * @return {Number} index of the key.
12598  */
12599     indexOfKey : function(key){
12600         if(!this.keys.indexOf){
12601             for(var i = 0, len = this.keys.length; i < len; i++){
12602                 if(this.keys[i] == key) return i;
12603             }
12604             return -1;
12605         }else{
12606             return this.keys.indexOf(key);
12607         }
12608     },
12609    
12610 /**
12611  * Returns the item associated with the passed key OR index. Key has priority over index.
12612  * @param {String/Number} key The key or index of the item.
12613  * @return {Object} The item associated with the passed key.
12614  */
12615     item : function(key){
12616         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12617         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12618     },
12619     
12620 /**
12621  * Returns the item at the specified index.
12622  * @param {Number} index The index of the item.
12623  * @return {Object}
12624  */
12625     itemAt : function(index){
12626         return this.items[index];
12627     },
12628     
12629 /**
12630  * Returns the item associated with the passed key.
12631  * @param {String/Number} key The key of the item.
12632  * @return {Object} The item associated with the passed key.
12633  */
12634     key : function(key){
12635         return this.map[key];
12636     },
12637    
12638 /**
12639  * Returns true if the collection contains the passed Object as an item.
12640  * @param {Object} o  The Object to look for in the collection.
12641  * @return {Boolean} True if the collection contains the Object as an item.
12642  */
12643     contains : function(o){
12644         return this.indexOf(o) != -1;
12645     },
12646    
12647 /**
12648  * Returns true if the collection contains the passed Object as a key.
12649  * @param {String} key The key to look for in the collection.
12650  * @return {Boolean} True if the collection contains the Object as a key.
12651  */
12652     containsKey : function(key){
12653         return typeof this.map[key] != "undefined";
12654     },
12655    
12656 /**
12657  * Removes all items from the collection.
12658  */
12659     clear : function(){
12660         this.length = 0;
12661         this.items = [];
12662         this.keys = [];
12663         this.map = {};
12664         this.fireEvent("clear");
12665     },
12666    
12667 /**
12668  * Returns the first item in the collection.
12669  * @return {Object} the first item in the collection..
12670  */
12671     first : function(){
12672         return this.items[0]; 
12673     },
12674    
12675 /**
12676  * Returns the last item in the collection.
12677  * @return {Object} the last item in the collection..
12678  */
12679     last : function(){
12680         return this.items[this.length-1];   
12681     },
12682     
12683     _sort : function(property, dir, fn){
12684         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12685         fn = fn || function(a, b){
12686             return a-b;
12687         };
12688         var c = [], k = this.keys, items = this.items;
12689         for(var i = 0, len = items.length; i < len; i++){
12690             c[c.length] = {key: k[i], value: items[i], index: i};
12691         }
12692         c.sort(function(a, b){
12693             var v = fn(a[property], b[property]) * dsc;
12694             if(v == 0){
12695                 v = (a.index < b.index ? -1 : 1);
12696             }
12697             return v;
12698         });
12699         for(var i = 0, len = c.length; i < len; i++){
12700             items[i] = c[i].value;
12701             k[i] = c[i].key;
12702         }
12703         this.fireEvent("sort", this);
12704     },
12705     
12706     /**
12707      * Sorts this collection with the passed comparison function
12708      * @param {String} direction (optional) "ASC" or "DESC"
12709      * @param {Function} fn (optional) comparison function
12710      */
12711     sort : function(dir, fn){
12712         this._sort("value", dir, fn);
12713     },
12714     
12715     /**
12716      * Sorts this collection by keys
12717      * @param {String} direction (optional) "ASC" or "DESC"
12718      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12719      */
12720     keySort : function(dir, fn){
12721         this._sort("key", dir, fn || function(a, b){
12722             return String(a).toUpperCase()-String(b).toUpperCase();
12723         });
12724     },
12725     
12726     /**
12727      * Returns a range of items in this collection
12728      * @param {Number} startIndex (optional) defaults to 0
12729      * @param {Number} endIndex (optional) default to the last item
12730      * @return {Array} An array of items
12731      */
12732     getRange : function(start, end){
12733         var items = this.items;
12734         if(items.length < 1){
12735             return [];
12736         }
12737         start = start || 0;
12738         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12739         var r = [];
12740         if(start <= end){
12741             for(var i = start; i <= end; i++) {
12742                     r[r.length] = items[i];
12743             }
12744         }else{
12745             for(var i = start; i >= end; i--) {
12746                     r[r.length] = items[i];
12747             }
12748         }
12749         return r;
12750     },
12751         
12752     /**
12753      * Filter the <i>objects</i> in this collection by a specific property. 
12754      * Returns a new collection that has been filtered.
12755      * @param {String} property A property on your objects
12756      * @param {String/RegExp} value Either string that the property values 
12757      * should start with or a RegExp to test against the property
12758      * @return {MixedCollection} The new filtered collection
12759      */
12760     filter : function(property, value){
12761         if(!value.exec){ // not a regex
12762             value = String(value);
12763             if(value.length == 0){
12764                 return this.clone();
12765             }
12766             value = new RegExp("^" + Roo.escapeRe(value), "i");
12767         }
12768         return this.filterBy(function(o){
12769             return o && value.test(o[property]);
12770         });
12771         },
12772     
12773     /**
12774      * Filter by a function. * Returns a new collection that has been filtered.
12775      * The passed function will be called with each 
12776      * object in the collection. If the function returns true, the value is included 
12777      * otherwise it is filtered.
12778      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12779      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12780      * @return {MixedCollection} The new filtered collection
12781      */
12782     filterBy : function(fn, scope){
12783         var r = new Roo.util.MixedCollection();
12784         r.getKey = this.getKey;
12785         var k = this.keys, it = this.items;
12786         for(var i = 0, len = it.length; i < len; i++){
12787             if(fn.call(scope||this, it[i], k[i])){
12788                                 r.add(k[i], it[i]);
12789                         }
12790         }
12791         return r;
12792     },
12793     
12794     /**
12795      * Creates a duplicate of this collection
12796      * @return {MixedCollection}
12797      */
12798     clone : function(){
12799         var r = new Roo.util.MixedCollection();
12800         var k = this.keys, it = this.items;
12801         for(var i = 0, len = it.length; i < len; i++){
12802             r.add(k[i], it[i]);
12803         }
12804         r.getKey = this.getKey;
12805         return r;
12806     }
12807 });
12808 /**
12809  * Returns the item associated with the passed key or index.
12810  * @method
12811  * @param {String/Number} key The key or index of the item.
12812  * @return {Object} The item associated with the passed key.
12813  */
12814 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12815  * Based on:
12816  * Ext JS Library 1.1.1
12817  * Copyright(c) 2006-2007, Ext JS, LLC.
12818  *
12819  * Originally Released Under LGPL - original licence link has changed is not relivant.
12820  *
12821  * Fork - LGPL
12822  * <script type="text/javascript">
12823  */
12824 /**
12825  * @class Roo.util.JSON
12826  * Modified version of Douglas Crockford"s json.js that doesn"t
12827  * mess with the Object prototype 
12828  * http://www.json.org/js.html
12829  * @singleton
12830  */
12831 Roo.util.JSON = new (function(){
12832     var useHasOwn = {}.hasOwnProperty ? true : false;
12833     
12834     // crashes Safari in some instances
12835     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12836     
12837     var pad = function(n) {
12838         return n < 10 ? "0" + n : n;
12839     };
12840     
12841     var m = {
12842         "\b": '\\b',
12843         "\t": '\\t',
12844         "\n": '\\n',
12845         "\f": '\\f',
12846         "\r": '\\r',
12847         '"' : '\\"',
12848         "\\": '\\\\'
12849     };
12850
12851     var encodeString = function(s){
12852         if (/["\\\x00-\x1f]/.test(s)) {
12853             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12854                 var c = m[b];
12855                 if(c){
12856                     return c;
12857                 }
12858                 c = b.charCodeAt();
12859                 return "\\u00" +
12860                     Math.floor(c / 16).toString(16) +
12861                     (c % 16).toString(16);
12862             }) + '"';
12863         }
12864         return '"' + s + '"';
12865     };
12866     
12867     var encodeArray = function(o){
12868         var a = ["["], b, i, l = o.length, v;
12869             for (i = 0; i < l; i += 1) {
12870                 v = o[i];
12871                 switch (typeof v) {
12872                     case "undefined":
12873                     case "function":
12874                     case "unknown":
12875                         break;
12876                     default:
12877                         if (b) {
12878                             a.push(',');
12879                         }
12880                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12881                         b = true;
12882                 }
12883             }
12884             a.push("]");
12885             return a.join("");
12886     };
12887     
12888     var encodeDate = function(o){
12889         return '"' + o.getFullYear() + "-" +
12890                 pad(o.getMonth() + 1) + "-" +
12891                 pad(o.getDate()) + "T" +
12892                 pad(o.getHours()) + ":" +
12893                 pad(o.getMinutes()) + ":" +
12894                 pad(o.getSeconds()) + '"';
12895     };
12896     
12897     /**
12898      * Encodes an Object, Array or other value
12899      * @param {Mixed} o The variable to encode
12900      * @return {String} The JSON string
12901      */
12902     this.encode = function(o)
12903     {
12904         // should this be extended to fully wrap stringify..
12905         
12906         if(typeof o == "undefined" || o === null){
12907             return "null";
12908         }else if(o instanceof Array){
12909             return encodeArray(o);
12910         }else if(o instanceof Date){
12911             return encodeDate(o);
12912         }else if(typeof o == "string"){
12913             return encodeString(o);
12914         }else if(typeof o == "number"){
12915             return isFinite(o) ? String(o) : "null";
12916         }else if(typeof o == "boolean"){
12917             return String(o);
12918         }else {
12919             var a = ["{"], b, i, v;
12920             for (i in o) {
12921                 if(!useHasOwn || o.hasOwnProperty(i)) {
12922                     v = o[i];
12923                     switch (typeof v) {
12924                     case "undefined":
12925                     case "function":
12926                     case "unknown":
12927                         break;
12928                     default:
12929                         if(b){
12930                             a.push(',');
12931                         }
12932                         a.push(this.encode(i), ":",
12933                                 v === null ? "null" : this.encode(v));
12934                         b = true;
12935                     }
12936                 }
12937             }
12938             a.push("}");
12939             return a.join("");
12940         }
12941     };
12942     
12943     /**
12944      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12945      * @param {String} json The JSON string
12946      * @return {Object} The resulting object
12947      */
12948     this.decode = function(json){
12949         
12950         return  /** eval:var:json */ eval("(" + json + ')');
12951     };
12952 })();
12953 /** 
12954  * Shorthand for {@link Roo.util.JSON#encode}
12955  * @member Roo encode 
12956  * @method */
12957 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12958 /** 
12959  * Shorthand for {@link Roo.util.JSON#decode}
12960  * @member Roo decode 
12961  * @method */
12962 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12963 /*
12964  * Based on:
12965  * Ext JS Library 1.1.1
12966  * Copyright(c) 2006-2007, Ext JS, LLC.
12967  *
12968  * Originally Released Under LGPL - original licence link has changed is not relivant.
12969  *
12970  * Fork - LGPL
12971  * <script type="text/javascript">
12972  */
12973  
12974 /**
12975  * @class Roo.util.Format
12976  * Reusable data formatting functions
12977  * @singleton
12978  */
12979 Roo.util.Format = function(){
12980     var trimRe = /^\s+|\s+$/g;
12981     return {
12982         /**
12983          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12984          * @param {String} value The string to truncate
12985          * @param {Number} length The maximum length to allow before truncating
12986          * @return {String} The converted text
12987          */
12988         ellipsis : function(value, len){
12989             if(value && value.length > len){
12990                 return value.substr(0, len-3)+"...";
12991             }
12992             return value;
12993         },
12994
12995         /**
12996          * Checks a reference and converts it to empty string if it is undefined
12997          * @param {Mixed} value Reference to check
12998          * @return {Mixed} Empty string if converted, otherwise the original value
12999          */
13000         undef : function(value){
13001             return typeof value != "undefined" ? value : "";
13002         },
13003
13004         /**
13005          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13006          * @param {String} value The string to encode
13007          * @return {String} The encoded text
13008          */
13009         htmlEncode : function(value){
13010             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13011         },
13012
13013         /**
13014          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13015          * @param {String} value The string to decode
13016          * @return {String} The decoded text
13017          */
13018         htmlDecode : function(value){
13019             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13020         },
13021
13022         /**
13023          * Trims any whitespace from either side of a string
13024          * @param {String} value The text to trim
13025          * @return {String} The trimmed text
13026          */
13027         trim : function(value){
13028             return String(value).replace(trimRe, "");
13029         },
13030
13031         /**
13032          * Returns a substring from within an original string
13033          * @param {String} value The original text
13034          * @param {Number} start The start index of the substring
13035          * @param {Number} length The length of the substring
13036          * @return {String} The substring
13037          */
13038         substr : function(value, start, length){
13039             return String(value).substr(start, length);
13040         },
13041
13042         /**
13043          * Converts a string to all lower case letters
13044          * @param {String} value The text to convert
13045          * @return {String} The converted text
13046          */
13047         lowercase : function(value){
13048             return String(value).toLowerCase();
13049         },
13050
13051         /**
13052          * Converts a string to all upper case letters
13053          * @param {String} value The text to convert
13054          * @return {String} The converted text
13055          */
13056         uppercase : function(value){
13057             return String(value).toUpperCase();
13058         },
13059
13060         /**
13061          * Converts the first character only of a string to upper case
13062          * @param {String} value The text to convert
13063          * @return {String} The converted text
13064          */
13065         capitalize : function(value){
13066             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13067         },
13068
13069         // private
13070         call : function(value, fn){
13071             if(arguments.length > 2){
13072                 var args = Array.prototype.slice.call(arguments, 2);
13073                 args.unshift(value);
13074                  
13075                 return /** eval:var:value */  eval(fn).apply(window, args);
13076             }else{
13077                 /** eval:var:value */
13078                 return /** eval:var:value */ eval(fn).call(window, value);
13079             }
13080         },
13081
13082        
13083         /**
13084          * safer version of Math.toFixed..??/
13085          * @param {Number/String} value The numeric value to format
13086          * @param {Number/String} value Decimal places 
13087          * @return {String} The formatted currency string
13088          */
13089         toFixed : function(v, n)
13090         {
13091             // why not use to fixed - precision is buggered???
13092             if (!n) {
13093                 return Math.round(v-0);
13094             }
13095             var fact = Math.pow(10,n+1);
13096             v = (Math.round((v-0)*fact))/fact;
13097             var z = (''+fact).substring(2);
13098             if (v == Math.floor(v)) {
13099                 return Math.floor(v) + '.' + z;
13100             }
13101             
13102             // now just padd decimals..
13103             var ps = String(v).split('.');
13104             var fd = (ps[1] + z);
13105             var r = fd.substring(0,n); 
13106             var rm = fd.substring(n); 
13107             if (rm < 5) {
13108                 return ps[0] + '.' + r;
13109             }
13110             r*=1; // turn it into a number;
13111             r++;
13112             if (String(r).length != n) {
13113                 ps[0]*=1;
13114                 ps[0]++;
13115                 r = String(r).substring(1); // chop the end off.
13116             }
13117             
13118             return ps[0] + '.' + r;
13119              
13120         },
13121         
13122         /**
13123          * Format a number as US currency
13124          * @param {Number/String} value The numeric value to format
13125          * @return {String} The formatted currency string
13126          */
13127         usMoney : function(v){
13128             v = (Math.round((v-0)*100))/100;
13129             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13130             v = String(v);
13131             var ps = v.split('.');
13132             var whole = ps[0];
13133             var sub = ps[1] ? '.'+ ps[1] : '.00';
13134             var r = /(\d+)(\d{3})/;
13135             while (r.test(whole)) {
13136                 whole = whole.replace(r, '$1' + ',' + '$2');
13137             }
13138             return "$" + whole + sub ;
13139         },
13140         
13141         /**
13142          * Parse a value into a formatted date using the specified format pattern.
13143          * @param {Mixed} value The value to format
13144          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13145          * @return {String} The formatted date string
13146          */
13147         date : function(v, format){
13148             if(!v){
13149                 return "";
13150             }
13151             if(!(v instanceof Date)){
13152                 v = new Date(Date.parse(v));
13153             }
13154             return v.dateFormat(format || "m/d/Y");
13155         },
13156
13157         /**
13158          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13159          * @param {String} format Any valid date format string
13160          * @return {Function} The date formatting function
13161          */
13162         dateRenderer : function(format){
13163             return function(v){
13164                 return Roo.util.Format.date(v, format);  
13165             };
13166         },
13167
13168         // private
13169         stripTagsRE : /<\/?[^>]+>/gi,
13170         
13171         /**
13172          * Strips all HTML tags
13173          * @param {Mixed} value The text from which to strip tags
13174          * @return {String} The stripped text
13175          */
13176         stripTags : function(v){
13177             return !v ? v : String(v).replace(this.stripTagsRE, "");
13178         }
13179     };
13180 }();/*
13181  * Based on:
13182  * Ext JS Library 1.1.1
13183  * Copyright(c) 2006-2007, Ext JS, LLC.
13184  *
13185  * Originally Released Under LGPL - original licence link has changed is not relivant.
13186  *
13187  * Fork - LGPL
13188  * <script type="text/javascript">
13189  */
13190
13191
13192  
13193
13194 /**
13195  * @class Roo.MasterTemplate
13196  * @extends Roo.Template
13197  * Provides a template that can have child templates. The syntax is:
13198 <pre><code>
13199 var t = new Roo.MasterTemplate(
13200         '&lt;select name="{name}"&gt;',
13201                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13202         '&lt;/select&gt;'
13203 );
13204 t.add('options', {value: 'foo', text: 'bar'});
13205 // or you can add multiple child elements in one shot
13206 t.addAll('options', [
13207     {value: 'foo', text: 'bar'},
13208     {value: 'foo2', text: 'bar2'},
13209     {value: 'foo3', text: 'bar3'}
13210 ]);
13211 // then append, applying the master template values
13212 t.append('my-form', {name: 'my-select'});
13213 </code></pre>
13214 * A name attribute for the child template is not required if you have only one child
13215 * template or you want to refer to them by index.
13216  */
13217 Roo.MasterTemplate = function(){
13218     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13219     this.originalHtml = this.html;
13220     var st = {};
13221     var m, re = this.subTemplateRe;
13222     re.lastIndex = 0;
13223     var subIndex = 0;
13224     while(m = re.exec(this.html)){
13225         var name = m[1], content = m[2];
13226         st[subIndex] = {
13227             name: name,
13228             index: subIndex,
13229             buffer: [],
13230             tpl : new Roo.Template(content)
13231         };
13232         if(name){
13233             st[name] = st[subIndex];
13234         }
13235         st[subIndex].tpl.compile();
13236         st[subIndex].tpl.call = this.call.createDelegate(this);
13237         subIndex++;
13238     }
13239     this.subCount = subIndex;
13240     this.subs = st;
13241 };
13242 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13243     /**
13244     * The regular expression used to match sub templates
13245     * @type RegExp
13246     * @property
13247     */
13248     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13249
13250     /**
13251      * Applies the passed values to a child template.
13252      * @param {String/Number} name (optional) The name or index of the child template
13253      * @param {Array/Object} values The values to be applied to the template
13254      * @return {MasterTemplate} this
13255      */
13256      add : function(name, values){
13257         if(arguments.length == 1){
13258             values = arguments[0];
13259             name = 0;
13260         }
13261         var s = this.subs[name];
13262         s.buffer[s.buffer.length] = s.tpl.apply(values);
13263         return this;
13264     },
13265
13266     /**
13267      * Applies all the passed values to a child template.
13268      * @param {String/Number} name (optional) The name or index of the child template
13269      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13270      * @param {Boolean} reset (optional) True to reset the template first
13271      * @return {MasterTemplate} this
13272      */
13273     fill : function(name, values, reset){
13274         var a = arguments;
13275         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13276             values = a[0];
13277             name = 0;
13278             reset = a[1];
13279         }
13280         if(reset){
13281             this.reset();
13282         }
13283         for(var i = 0, len = values.length; i < len; i++){
13284             this.add(name, values[i]);
13285         }
13286         return this;
13287     },
13288
13289     /**
13290      * Resets the template for reuse
13291      * @return {MasterTemplate} this
13292      */
13293      reset : function(){
13294         var s = this.subs;
13295         for(var i = 0; i < this.subCount; i++){
13296             s[i].buffer = [];
13297         }
13298         return this;
13299     },
13300
13301     applyTemplate : function(values){
13302         var s = this.subs;
13303         var replaceIndex = -1;
13304         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13305             return s[++replaceIndex].buffer.join("");
13306         });
13307         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13308     },
13309
13310     apply : function(){
13311         return this.applyTemplate.apply(this, arguments);
13312     },
13313
13314     compile : function(){return this;}
13315 });
13316
13317 /**
13318  * Alias for fill().
13319  * @method
13320  */
13321 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13322  /**
13323  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13324  * var tpl = Roo.MasterTemplate.from('element-id');
13325  * @param {String/HTMLElement} el
13326  * @param {Object} config
13327  * @static
13328  */
13329 Roo.MasterTemplate.from = function(el, config){
13330     el = Roo.getDom(el);
13331     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13332 };/*
13333  * Based on:
13334  * Ext JS Library 1.1.1
13335  * Copyright(c) 2006-2007, Ext JS, LLC.
13336  *
13337  * Originally Released Under LGPL - original licence link has changed is not relivant.
13338  *
13339  * Fork - LGPL
13340  * <script type="text/javascript">
13341  */
13342
13343  
13344 /**
13345  * @class Roo.util.CSS
13346  * Utility class for manipulating CSS rules
13347  * @singleton
13348  */
13349 Roo.util.CSS = function(){
13350         var rules = null;
13351         var doc = document;
13352
13353     var camelRe = /(-[a-z])/gi;
13354     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13355
13356    return {
13357    /**
13358     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13359     * tag and appended to the HEAD of the document.
13360     * @param {String|Object} cssText The text containing the css rules
13361     * @param {String} id An id to add to the stylesheet for later removal
13362     * @return {StyleSheet}
13363     */
13364     createStyleSheet : function(cssText, id){
13365         var ss;
13366         var head = doc.getElementsByTagName("head")[0];
13367         var nrules = doc.createElement("style");
13368         nrules.setAttribute("type", "text/css");
13369         if(id){
13370             nrules.setAttribute("id", id);
13371         }
13372         if (typeof(cssText) != 'string') {
13373             // support object maps..
13374             // not sure if this a good idea.. 
13375             // perhaps it should be merged with the general css handling
13376             // and handle js style props.
13377             var cssTextNew = [];
13378             for(var n in cssText) {
13379                 var citems = [];
13380                 for(var k in cssText[n]) {
13381                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13382                 }
13383                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13384                 
13385             }
13386             cssText = cssTextNew.join("\n");
13387             
13388         }
13389        
13390        
13391        if(Roo.isIE){
13392            head.appendChild(nrules);
13393            ss = nrules.styleSheet;
13394            ss.cssText = cssText;
13395        }else{
13396            try{
13397                 nrules.appendChild(doc.createTextNode(cssText));
13398            }catch(e){
13399                nrules.cssText = cssText; 
13400            }
13401            head.appendChild(nrules);
13402            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13403        }
13404        this.cacheStyleSheet(ss);
13405        return ss;
13406    },
13407
13408    /**
13409     * Removes a style or link tag by id
13410     * @param {String} id The id of the tag
13411     */
13412    removeStyleSheet : function(id){
13413        var existing = doc.getElementById(id);
13414        if(existing){
13415            existing.parentNode.removeChild(existing);
13416        }
13417    },
13418
13419    /**
13420     * Dynamically swaps an existing stylesheet reference for a new one
13421     * @param {String} id The id of an existing link tag to remove
13422     * @param {String} url The href of the new stylesheet to include
13423     */
13424    swapStyleSheet : function(id, url){
13425        this.removeStyleSheet(id);
13426        var ss = doc.createElement("link");
13427        ss.setAttribute("rel", "stylesheet");
13428        ss.setAttribute("type", "text/css");
13429        ss.setAttribute("id", id);
13430        ss.setAttribute("href", url);
13431        doc.getElementsByTagName("head")[0].appendChild(ss);
13432    },
13433    
13434    /**
13435     * Refresh the rule cache if you have dynamically added stylesheets
13436     * @return {Object} An object (hash) of rules indexed by selector
13437     */
13438    refreshCache : function(){
13439        return this.getRules(true);
13440    },
13441
13442    // private
13443    cacheStyleSheet : function(stylesheet){
13444        if(!rules){
13445            rules = {};
13446        }
13447        try{// try catch for cross domain access issue
13448            var ssRules = stylesheet.cssRules || stylesheet.rules;
13449            for(var j = ssRules.length-1; j >= 0; --j){
13450                rules[ssRules[j].selectorText] = ssRules[j];
13451            }
13452        }catch(e){}
13453    },
13454    
13455    /**
13456     * Gets all css rules for the document
13457     * @param {Boolean} refreshCache true to refresh the internal cache
13458     * @return {Object} An object (hash) of rules indexed by selector
13459     */
13460    getRules : function(refreshCache){
13461                 if(rules == null || refreshCache){
13462                         rules = {};
13463                         var ds = doc.styleSheets;
13464                         for(var i =0, len = ds.length; i < len; i++){
13465                             try{
13466                         this.cacheStyleSheet(ds[i]);
13467                     }catch(e){} 
13468                 }
13469                 }
13470                 return rules;
13471         },
13472         
13473         /**
13474     * Gets an an individual CSS rule by selector(s)
13475     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13476     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13477     * @return {CSSRule} The CSS rule or null if one is not found
13478     */
13479    getRule : function(selector, refreshCache){
13480                 var rs = this.getRules(refreshCache);
13481                 if(!(selector instanceof Array)){
13482                     return rs[selector];
13483                 }
13484                 for(var i = 0; i < selector.length; i++){
13485                         if(rs[selector[i]]){
13486                                 return rs[selector[i]];
13487                         }
13488                 }
13489                 return null;
13490         },
13491         
13492         
13493         /**
13494     * Updates a rule property
13495     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13496     * @param {String} property The css property
13497     * @param {String} value The new value for the property
13498     * @return {Boolean} true If a rule was found and updated
13499     */
13500    updateRule : function(selector, property, value){
13501                 if(!(selector instanceof Array)){
13502                         var rule = this.getRule(selector);
13503                         if(rule){
13504                                 rule.style[property.replace(camelRe, camelFn)] = value;
13505                                 return true;
13506                         }
13507                 }else{
13508                         for(var i = 0; i < selector.length; i++){
13509                                 if(this.updateRule(selector[i], property, value)){
13510                                         return true;
13511                                 }
13512                         }
13513                 }
13514                 return false;
13515         }
13516    };   
13517 }();/*
13518  * Based on:
13519  * Ext JS Library 1.1.1
13520  * Copyright(c) 2006-2007, Ext JS, LLC.
13521  *
13522  * Originally Released Under LGPL - original licence link has changed is not relivant.
13523  *
13524  * Fork - LGPL
13525  * <script type="text/javascript">
13526  */
13527
13528  
13529
13530 /**
13531  * @class Roo.util.ClickRepeater
13532  * @extends Roo.util.Observable
13533  * 
13534  * A wrapper class which can be applied to any element. Fires a "click" event while the
13535  * mouse is pressed. The interval between firings may be specified in the config but
13536  * defaults to 10 milliseconds.
13537  * 
13538  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13539  * 
13540  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13541  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13542  * Similar to an autorepeat key delay.
13543  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13544  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13545  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13546  *           "interval" and "delay" are ignored. "immediate" is honored.
13547  * @cfg {Boolean} preventDefault True to prevent the default click event
13548  * @cfg {Boolean} stopDefault True to stop the default click event
13549  * 
13550  * @history
13551  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13552  *     2007-02-02 jvs Renamed to ClickRepeater
13553  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13554  *
13555  *  @constructor
13556  * @param {String/HTMLElement/Element} el The element to listen on
13557  * @param {Object} config
13558  **/
13559 Roo.util.ClickRepeater = function(el, config)
13560 {
13561     this.el = Roo.get(el);
13562     this.el.unselectable();
13563
13564     Roo.apply(this, config);
13565
13566     this.addEvents({
13567     /**
13568      * @event mousedown
13569      * Fires when the mouse button is depressed.
13570      * @param {Roo.util.ClickRepeater} this
13571      */
13572         "mousedown" : true,
13573     /**
13574      * @event click
13575      * Fires on a specified interval during the time the element is pressed.
13576      * @param {Roo.util.ClickRepeater} this
13577      */
13578         "click" : true,
13579     /**
13580      * @event mouseup
13581      * Fires when the mouse key is released.
13582      * @param {Roo.util.ClickRepeater} this
13583      */
13584         "mouseup" : true
13585     });
13586
13587     this.el.on("mousedown", this.handleMouseDown, this);
13588     if(this.preventDefault || this.stopDefault){
13589         this.el.on("click", function(e){
13590             if(this.preventDefault){
13591                 e.preventDefault();
13592             }
13593             if(this.stopDefault){
13594                 e.stopEvent();
13595             }
13596         }, this);
13597     }
13598
13599     // allow inline handler
13600     if(this.handler){
13601         this.on("click", this.handler,  this.scope || this);
13602     }
13603
13604     Roo.util.ClickRepeater.superclass.constructor.call(this);
13605 };
13606
13607 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13608     interval : 20,
13609     delay: 250,
13610     preventDefault : true,
13611     stopDefault : false,
13612     timer : 0,
13613
13614     // private
13615     handleMouseDown : function(){
13616         clearTimeout(this.timer);
13617         this.el.blur();
13618         if(this.pressClass){
13619             this.el.addClass(this.pressClass);
13620         }
13621         this.mousedownTime = new Date();
13622
13623         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13624         this.el.on("mouseout", this.handleMouseOut, this);
13625
13626         this.fireEvent("mousedown", this);
13627         this.fireEvent("click", this);
13628         
13629         this.timer = this.click.defer(this.delay || this.interval, this);
13630     },
13631
13632     // private
13633     click : function(){
13634         this.fireEvent("click", this);
13635         this.timer = this.click.defer(this.getInterval(), this);
13636     },
13637
13638     // private
13639     getInterval: function(){
13640         if(!this.accelerate){
13641             return this.interval;
13642         }
13643         var pressTime = this.mousedownTime.getElapsed();
13644         if(pressTime < 500){
13645             return 400;
13646         }else if(pressTime < 1700){
13647             return 320;
13648         }else if(pressTime < 2600){
13649             return 250;
13650         }else if(pressTime < 3500){
13651             return 180;
13652         }else if(pressTime < 4400){
13653             return 140;
13654         }else if(pressTime < 5300){
13655             return 80;
13656         }else if(pressTime < 6200){
13657             return 50;
13658         }else{
13659             return 10;
13660         }
13661     },
13662
13663     // private
13664     handleMouseOut : function(){
13665         clearTimeout(this.timer);
13666         if(this.pressClass){
13667             this.el.removeClass(this.pressClass);
13668         }
13669         this.el.on("mouseover", this.handleMouseReturn, this);
13670     },
13671
13672     // private
13673     handleMouseReturn : function(){
13674         this.el.un("mouseover", this.handleMouseReturn);
13675         if(this.pressClass){
13676             this.el.addClass(this.pressClass);
13677         }
13678         this.click();
13679     },
13680
13681     // private
13682     handleMouseUp : function(){
13683         clearTimeout(this.timer);
13684         this.el.un("mouseover", this.handleMouseReturn);
13685         this.el.un("mouseout", this.handleMouseOut);
13686         Roo.get(document).un("mouseup", this.handleMouseUp);
13687         this.el.removeClass(this.pressClass);
13688         this.fireEvent("mouseup", this);
13689     }
13690 });/*
13691  * Based on:
13692  * Ext JS Library 1.1.1
13693  * Copyright(c) 2006-2007, Ext JS, LLC.
13694  *
13695  * Originally Released Under LGPL - original licence link has changed is not relivant.
13696  *
13697  * Fork - LGPL
13698  * <script type="text/javascript">
13699  */
13700
13701  
13702 /**
13703  * @class Roo.KeyNav
13704  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13705  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13706  * way to implement custom navigation schemes for any UI component.</p>
13707  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13708  * pageUp, pageDown, del, home, end.  Usage:</p>
13709  <pre><code>
13710 var nav = new Roo.KeyNav("my-element", {
13711     "left" : function(e){
13712         this.moveLeft(e.ctrlKey);
13713     },
13714     "right" : function(e){
13715         this.moveRight(e.ctrlKey);
13716     },
13717     "enter" : function(e){
13718         this.save();
13719     },
13720     scope : this
13721 });
13722 </code></pre>
13723  * @constructor
13724  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13725  * @param {Object} config The config
13726  */
13727 Roo.KeyNav = function(el, config){
13728     this.el = Roo.get(el);
13729     Roo.apply(this, config);
13730     if(!this.disabled){
13731         this.disabled = true;
13732         this.enable();
13733     }
13734 };
13735
13736 Roo.KeyNav.prototype = {
13737     /**
13738      * @cfg {Boolean} disabled
13739      * True to disable this KeyNav instance (defaults to false)
13740      */
13741     disabled : false,
13742     /**
13743      * @cfg {String} defaultEventAction
13744      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13745      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13746      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13747      */
13748     defaultEventAction: "stopEvent",
13749     /**
13750      * @cfg {Boolean} forceKeyDown
13751      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13752      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13753      * handle keydown instead of keypress.
13754      */
13755     forceKeyDown : false,
13756
13757     // private
13758     prepareEvent : function(e){
13759         var k = e.getKey();
13760         var h = this.keyToHandler[k];
13761         //if(h && this[h]){
13762         //    e.stopPropagation();
13763         //}
13764         if(Roo.isSafari && h && k >= 37 && k <= 40){
13765             e.stopEvent();
13766         }
13767     },
13768
13769     // private
13770     relay : function(e){
13771         var k = e.getKey();
13772         var h = this.keyToHandler[k];
13773         if(h && this[h]){
13774             if(this.doRelay(e, this[h], h) !== true){
13775                 e[this.defaultEventAction]();
13776             }
13777         }
13778     },
13779
13780     // private
13781     doRelay : function(e, h, hname){
13782         return h.call(this.scope || this, e);
13783     },
13784
13785     // possible handlers
13786     enter : false,
13787     left : false,
13788     right : false,
13789     up : false,
13790     down : false,
13791     tab : false,
13792     esc : false,
13793     pageUp : false,
13794     pageDown : false,
13795     del : false,
13796     home : false,
13797     end : false,
13798
13799     // quick lookup hash
13800     keyToHandler : {
13801         37 : "left",
13802         39 : "right",
13803         38 : "up",
13804         40 : "down",
13805         33 : "pageUp",
13806         34 : "pageDown",
13807         46 : "del",
13808         36 : "home",
13809         35 : "end",
13810         13 : "enter",
13811         27 : "esc",
13812         9  : "tab"
13813     },
13814
13815         /**
13816          * Enable this KeyNav
13817          */
13818         enable: function(){
13819                 if(this.disabled){
13820             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13821             // the EventObject will normalize Safari automatically
13822             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13823                 this.el.on("keydown", this.relay,  this);
13824             }else{
13825                 this.el.on("keydown", this.prepareEvent,  this);
13826                 this.el.on("keypress", this.relay,  this);
13827             }
13828                     this.disabled = false;
13829                 }
13830         },
13831
13832         /**
13833          * Disable this KeyNav
13834          */
13835         disable: function(){
13836                 if(!this.disabled){
13837                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13838                 this.el.un("keydown", this.relay);
13839             }else{
13840                 this.el.un("keydown", this.prepareEvent);
13841                 this.el.un("keypress", this.relay);
13842             }
13843                     this.disabled = true;
13844                 }
13845         }
13846 };/*
13847  * Based on:
13848  * Ext JS Library 1.1.1
13849  * Copyright(c) 2006-2007, Ext JS, LLC.
13850  *
13851  * Originally Released Under LGPL - original licence link has changed is not relivant.
13852  *
13853  * Fork - LGPL
13854  * <script type="text/javascript">
13855  */
13856
13857  
13858 /**
13859  * @class Roo.KeyMap
13860  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13861  * The constructor accepts the same config object as defined by {@link #addBinding}.
13862  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13863  * combination it will call the function with this signature (if the match is a multi-key
13864  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13865  * A KeyMap can also handle a string representation of keys.<br />
13866  * Usage:
13867  <pre><code>
13868 // map one key by key code
13869 var map = new Roo.KeyMap("my-element", {
13870     key: 13, // or Roo.EventObject.ENTER
13871     fn: myHandler,
13872     scope: myObject
13873 });
13874
13875 // map multiple keys to one action by string
13876 var map = new Roo.KeyMap("my-element", {
13877     key: "a\r\n\t",
13878     fn: myHandler,
13879     scope: myObject
13880 });
13881
13882 // map multiple keys to multiple actions by strings and array of codes
13883 var map = new Roo.KeyMap("my-element", [
13884     {
13885         key: [10,13],
13886         fn: function(){ alert("Return was pressed"); }
13887     }, {
13888         key: "abc",
13889         fn: function(){ alert('a, b or c was pressed'); }
13890     }, {
13891         key: "\t",
13892         ctrl:true,
13893         shift:true,
13894         fn: function(){ alert('Control + shift + tab was pressed.'); }
13895     }
13896 ]);
13897 </code></pre>
13898  * <b>Note: A KeyMap starts enabled</b>
13899  * @constructor
13900  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13901  * @param {Object} config The config (see {@link #addBinding})
13902  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13903  */
13904 Roo.KeyMap = function(el, config, eventName){
13905     this.el  = Roo.get(el);
13906     this.eventName = eventName || "keydown";
13907     this.bindings = [];
13908     if(config){
13909         this.addBinding(config);
13910     }
13911     this.enable();
13912 };
13913
13914 Roo.KeyMap.prototype = {
13915     /**
13916      * True to stop the event from bubbling and prevent the default browser action if the
13917      * key was handled by the KeyMap (defaults to false)
13918      * @type Boolean
13919      */
13920     stopEvent : false,
13921
13922     /**
13923      * Add a new binding to this KeyMap. The following config object properties are supported:
13924      * <pre>
13925 Property    Type             Description
13926 ----------  ---------------  ----------------------------------------------------------------------
13927 key         String/Array     A single keycode or an array of keycodes to handle
13928 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13929 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13930 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13931 fn          Function         The function to call when KeyMap finds the expected key combination
13932 scope       Object           The scope of the callback function
13933 </pre>
13934      *
13935      * Usage:
13936      * <pre><code>
13937 // Create a KeyMap
13938 var map = new Roo.KeyMap(document, {
13939     key: Roo.EventObject.ENTER,
13940     fn: handleKey,
13941     scope: this
13942 });
13943
13944 //Add a new binding to the existing KeyMap later
13945 map.addBinding({
13946     key: 'abc',
13947     shift: true,
13948     fn: handleKey,
13949     scope: this
13950 });
13951 </code></pre>
13952      * @param {Object/Array} config A single KeyMap config or an array of configs
13953      */
13954         addBinding : function(config){
13955         if(config instanceof Array){
13956             for(var i = 0, len = config.length; i < len; i++){
13957                 this.addBinding(config[i]);
13958             }
13959             return;
13960         }
13961         var keyCode = config.key,
13962             shift = config.shift, 
13963             ctrl = config.ctrl, 
13964             alt = config.alt,
13965             fn = config.fn,
13966             scope = config.scope;
13967         if(typeof keyCode == "string"){
13968             var ks = [];
13969             var keyString = keyCode.toUpperCase();
13970             for(var j = 0, len = keyString.length; j < len; j++){
13971                 ks.push(keyString.charCodeAt(j));
13972             }
13973             keyCode = ks;
13974         }
13975         var keyArray = keyCode instanceof Array;
13976         var handler = function(e){
13977             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13978                 var k = e.getKey();
13979                 if(keyArray){
13980                     for(var i = 0, len = keyCode.length; i < len; i++){
13981                         if(keyCode[i] == k){
13982                           if(this.stopEvent){
13983                               e.stopEvent();
13984                           }
13985                           fn.call(scope || window, k, e);
13986                           return;
13987                         }
13988                     }
13989                 }else{
13990                     if(k == keyCode){
13991                         if(this.stopEvent){
13992                            e.stopEvent();
13993                         }
13994                         fn.call(scope || window, k, e);
13995                     }
13996                 }
13997             }
13998         };
13999         this.bindings.push(handler);  
14000         },
14001
14002     /**
14003      * Shorthand for adding a single key listener
14004      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14005      * following options:
14006      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14007      * @param {Function} fn The function to call
14008      * @param {Object} scope (optional) The scope of the function
14009      */
14010     on : function(key, fn, scope){
14011         var keyCode, shift, ctrl, alt;
14012         if(typeof key == "object" && !(key instanceof Array)){
14013             keyCode = key.key;
14014             shift = key.shift;
14015             ctrl = key.ctrl;
14016             alt = key.alt;
14017         }else{
14018             keyCode = key;
14019         }
14020         this.addBinding({
14021             key: keyCode,
14022             shift: shift,
14023             ctrl: ctrl,
14024             alt: alt,
14025             fn: fn,
14026             scope: scope
14027         })
14028     },
14029
14030     // private
14031     handleKeyDown : function(e){
14032             if(this.enabled){ //just in case
14033             var b = this.bindings;
14034             for(var i = 0, len = b.length; i < len; i++){
14035                 b[i].call(this, e);
14036             }
14037             }
14038         },
14039         
14040         /**
14041          * Returns true if this KeyMap is enabled
14042          * @return {Boolean} 
14043          */
14044         isEnabled : function(){
14045             return this.enabled;  
14046         },
14047         
14048         /**
14049          * Enables this KeyMap
14050          */
14051         enable: function(){
14052                 if(!this.enabled){
14053                     this.el.on(this.eventName, this.handleKeyDown, this);
14054                     this.enabled = true;
14055                 }
14056         },
14057
14058         /**
14059          * Disable this KeyMap
14060          */
14061         disable: function(){
14062                 if(this.enabled){
14063                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14064                     this.enabled = false;
14065                 }
14066         }
14067 };/*
14068  * Based on:
14069  * Ext JS Library 1.1.1
14070  * Copyright(c) 2006-2007, Ext JS, LLC.
14071  *
14072  * Originally Released Under LGPL - original licence link has changed is not relivant.
14073  *
14074  * Fork - LGPL
14075  * <script type="text/javascript">
14076  */
14077
14078  
14079 /**
14080  * @class Roo.util.TextMetrics
14081  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14082  * wide, in pixels, a given block of text will be.
14083  * @singleton
14084  */
14085 Roo.util.TextMetrics = function(){
14086     var shared;
14087     return {
14088         /**
14089          * Measures the size of the specified text
14090          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14091          * that can affect the size of the rendered text
14092          * @param {String} text The text to measure
14093          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14094          * in order to accurately measure the text height
14095          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14096          */
14097         measure : function(el, text, fixedWidth){
14098             if(!shared){
14099                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14100             }
14101             shared.bind(el);
14102             shared.setFixedWidth(fixedWidth || 'auto');
14103             return shared.getSize(text);
14104         },
14105
14106         /**
14107          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14108          * the overhead of multiple calls to initialize the style properties on each measurement.
14109          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14110          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14111          * in order to accurately measure the text height
14112          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14113          */
14114         createInstance : function(el, fixedWidth){
14115             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14116         }
14117     };
14118 }();
14119
14120  
14121
14122 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14123     var ml = new Roo.Element(document.createElement('div'));
14124     document.body.appendChild(ml.dom);
14125     ml.position('absolute');
14126     ml.setLeftTop(-1000, -1000);
14127     ml.hide();
14128
14129     if(fixedWidth){
14130         ml.setWidth(fixedWidth);
14131     }
14132      
14133     var instance = {
14134         /**
14135          * Returns the size of the specified text based on the internal element's style and width properties
14136          * @memberOf Roo.util.TextMetrics.Instance#
14137          * @param {String} text The text to measure
14138          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14139          */
14140         getSize : function(text){
14141             ml.update(text);
14142             var s = ml.getSize();
14143             ml.update('');
14144             return s;
14145         },
14146
14147         /**
14148          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14149          * that can affect the size of the rendered text
14150          * @memberOf Roo.util.TextMetrics.Instance#
14151          * @param {String/HTMLElement} el The element, dom node or id
14152          */
14153         bind : function(el){
14154             ml.setStyle(
14155                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14156             );
14157         },
14158
14159         /**
14160          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14161          * to set a fixed width in order to accurately measure the text height.
14162          * @memberOf Roo.util.TextMetrics.Instance#
14163          * @param {Number} width The width to set on the element
14164          */
14165         setFixedWidth : function(width){
14166             ml.setWidth(width);
14167         },
14168
14169         /**
14170          * Returns the measured width of the specified text
14171          * @memberOf Roo.util.TextMetrics.Instance#
14172          * @param {String} text The text to measure
14173          * @return {Number} width The width in pixels
14174          */
14175         getWidth : function(text){
14176             ml.dom.style.width = 'auto';
14177             return this.getSize(text).width;
14178         },
14179
14180         /**
14181          * Returns the measured height of the specified text.  For multiline text, be sure to call
14182          * {@link #setFixedWidth} if necessary.
14183          * @memberOf Roo.util.TextMetrics.Instance#
14184          * @param {String} text The text to measure
14185          * @return {Number} height The height in pixels
14186          */
14187         getHeight : function(text){
14188             return this.getSize(text).height;
14189         }
14190     };
14191
14192     instance.bind(bindTo);
14193
14194     return instance;
14195 };
14196
14197 // backwards compat
14198 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14199  * Based on:
14200  * Ext JS Library 1.1.1
14201  * Copyright(c) 2006-2007, Ext JS, LLC.
14202  *
14203  * Originally Released Under LGPL - original licence link has changed is not relivant.
14204  *
14205  * Fork - LGPL
14206  * <script type="text/javascript">
14207  */
14208
14209 /**
14210  * @class Roo.state.Provider
14211  * Abstract base class for state provider implementations. This class provides methods
14212  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14213  * Provider interface.
14214  */
14215 Roo.state.Provider = function(){
14216     /**
14217      * @event statechange
14218      * Fires when a state change occurs.
14219      * @param {Provider} this This state provider
14220      * @param {String} key The state key which was changed
14221      * @param {String} value The encoded value for the state
14222      */
14223     this.addEvents({
14224         "statechange": true
14225     });
14226     this.state = {};
14227     Roo.state.Provider.superclass.constructor.call(this);
14228 };
14229 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14230     /**
14231      * Returns the current value for a key
14232      * @param {String} name The key name
14233      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14234      * @return {Mixed} The state data
14235      */
14236     get : function(name, defaultValue){
14237         return typeof this.state[name] == "undefined" ?
14238             defaultValue : this.state[name];
14239     },
14240     
14241     /**
14242      * Clears a value from the state
14243      * @param {String} name The key name
14244      */
14245     clear : function(name){
14246         delete this.state[name];
14247         this.fireEvent("statechange", this, name, null);
14248     },
14249     
14250     /**
14251      * Sets the value for a key
14252      * @param {String} name The key name
14253      * @param {Mixed} value The value to set
14254      */
14255     set : function(name, value){
14256         this.state[name] = value;
14257         this.fireEvent("statechange", this, name, value);
14258     },
14259     
14260     /**
14261      * Decodes a string previously encoded with {@link #encodeValue}.
14262      * @param {String} value The value to decode
14263      * @return {Mixed} The decoded value
14264      */
14265     decodeValue : function(cookie){
14266         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14267         var matches = re.exec(unescape(cookie));
14268         if(!matches || !matches[1]) return; // non state cookie
14269         var type = matches[1];
14270         var v = matches[2];
14271         switch(type){
14272             case "n":
14273                 return parseFloat(v);
14274             case "d":
14275                 return new Date(Date.parse(v));
14276             case "b":
14277                 return (v == "1");
14278             case "a":
14279                 var all = [];
14280                 var values = v.split("^");
14281                 for(var i = 0, len = values.length; i < len; i++){
14282                     all.push(this.decodeValue(values[i]));
14283                 }
14284                 return all;
14285            case "o":
14286                 var all = {};
14287                 var values = v.split("^");
14288                 for(var i = 0, len = values.length; i < len; i++){
14289                     var kv = values[i].split("=");
14290                     all[kv[0]] = this.decodeValue(kv[1]);
14291                 }
14292                 return all;
14293            default:
14294                 return v;
14295         }
14296     },
14297     
14298     /**
14299      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14300      * @param {Mixed} value The value to encode
14301      * @return {String} The encoded value
14302      */
14303     encodeValue : function(v){
14304         var enc;
14305         if(typeof v == "number"){
14306             enc = "n:" + v;
14307         }else if(typeof v == "boolean"){
14308             enc = "b:" + (v ? "1" : "0");
14309         }else if(v instanceof Date){
14310             enc = "d:" + v.toGMTString();
14311         }else if(v instanceof Array){
14312             var flat = "";
14313             for(var i = 0, len = v.length; i < len; i++){
14314                 flat += this.encodeValue(v[i]);
14315                 if(i != len-1) flat += "^";
14316             }
14317             enc = "a:" + flat;
14318         }else if(typeof v == "object"){
14319             var flat = "";
14320             for(var key in v){
14321                 if(typeof v[key] != "function"){
14322                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14323                 }
14324             }
14325             enc = "o:" + flat.substring(0, flat.length-1);
14326         }else{
14327             enc = "s:" + v;
14328         }
14329         return escape(enc);        
14330     }
14331 });
14332
14333 /*
14334  * Based on:
14335  * Ext JS Library 1.1.1
14336  * Copyright(c) 2006-2007, Ext JS, LLC.
14337  *
14338  * Originally Released Under LGPL - original licence link has changed is not relivant.
14339  *
14340  * Fork - LGPL
14341  * <script type="text/javascript">
14342  */
14343 /**
14344  * @class Roo.state.Manager
14345  * This is the global state manager. By default all components that are "state aware" check this class
14346  * for state information if you don't pass them a custom state provider. In order for this class
14347  * to be useful, it must be initialized with a provider when your application initializes.
14348  <pre><code>
14349 // in your initialization function
14350 init : function(){
14351    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14352    ...
14353    // supposed you have a {@link Roo.BorderLayout}
14354    var layout = new Roo.BorderLayout(...);
14355    layout.restoreState();
14356    // or a {Roo.BasicDialog}
14357    var dialog = new Roo.BasicDialog(...);
14358    dialog.restoreState();
14359  </code></pre>
14360  * @singleton
14361  */
14362 Roo.state.Manager = function(){
14363     var provider = new Roo.state.Provider();
14364     
14365     return {
14366         /**
14367          * Configures the default state provider for your application
14368          * @param {Provider} stateProvider The state provider to set
14369          */
14370         setProvider : function(stateProvider){
14371             provider = stateProvider;
14372         },
14373         
14374         /**
14375          * Returns the current value for a key
14376          * @param {String} name The key name
14377          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14378          * @return {Mixed} The state data
14379          */
14380         get : function(key, defaultValue){
14381             return provider.get(key, defaultValue);
14382         },
14383         
14384         /**
14385          * Sets the value for a key
14386          * @param {String} name The key name
14387          * @param {Mixed} value The state data
14388          */
14389          set : function(key, value){
14390             provider.set(key, value);
14391         },
14392         
14393         /**
14394          * Clears a value from the state
14395          * @param {String} name The key name
14396          */
14397         clear : function(key){
14398             provider.clear(key);
14399         },
14400         
14401         /**
14402          * Gets the currently configured state provider
14403          * @return {Provider} The state provider
14404          */
14405         getProvider : function(){
14406             return provider;
14407         }
14408     };
14409 }();
14410 /*
14411  * Based on:
14412  * Ext JS Library 1.1.1
14413  * Copyright(c) 2006-2007, Ext JS, LLC.
14414  *
14415  * Originally Released Under LGPL - original licence link has changed is not relivant.
14416  *
14417  * Fork - LGPL
14418  * <script type="text/javascript">
14419  */
14420 /**
14421  * @class Roo.state.CookieProvider
14422  * @extends Roo.state.Provider
14423  * The default Provider implementation which saves state via cookies.
14424  * <br />Usage:
14425  <pre><code>
14426    var cp = new Roo.state.CookieProvider({
14427        path: "/cgi-bin/",
14428        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14429        domain: "roojs.com"
14430    })
14431    Roo.state.Manager.setProvider(cp);
14432  </code></pre>
14433  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14434  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14435  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14436  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14437  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14438  * domain the page is running on including the 'www' like 'www.roojs.com')
14439  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14440  * @constructor
14441  * Create a new CookieProvider
14442  * @param {Object} config The configuration object
14443  */
14444 Roo.state.CookieProvider = function(config){
14445     Roo.state.CookieProvider.superclass.constructor.call(this);
14446     this.path = "/";
14447     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14448     this.domain = null;
14449     this.secure = false;
14450     Roo.apply(this, config);
14451     this.state = this.readCookies();
14452 };
14453
14454 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14455     // private
14456     set : function(name, value){
14457         if(typeof value == "undefined" || value === null){
14458             this.clear(name);
14459             return;
14460         }
14461         this.setCookie(name, value);
14462         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14463     },
14464
14465     // private
14466     clear : function(name){
14467         this.clearCookie(name);
14468         Roo.state.CookieProvider.superclass.clear.call(this, name);
14469     },
14470
14471     // private
14472     readCookies : function(){
14473         var cookies = {};
14474         var c = document.cookie + ";";
14475         var re = /\s?(.*?)=(.*?);/g;
14476         var matches;
14477         while((matches = re.exec(c)) != null){
14478             var name = matches[1];
14479             var value = matches[2];
14480             if(name && name.substring(0,3) == "ys-"){
14481                 cookies[name.substr(3)] = this.decodeValue(value);
14482             }
14483         }
14484         return cookies;
14485     },
14486
14487     // private
14488     setCookie : function(name, value){
14489         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14490            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14491            ((this.path == null) ? "" : ("; path=" + this.path)) +
14492            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14493            ((this.secure == true) ? "; secure" : "");
14494     },
14495
14496     // private
14497     clearCookie : function(name){
14498         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14499            ((this.path == null) ? "" : ("; path=" + this.path)) +
14500            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14501            ((this.secure == true) ? "; secure" : "");
14502     }
14503 });/*
14504  * Based on:
14505  * Ext JS Library 1.1.1
14506  * Copyright(c) 2006-2007, Ext JS, LLC.
14507  *
14508  * Originally Released Under LGPL - original licence link has changed is not relivant.
14509  *
14510  * Fork - LGPL
14511  * <script type="text/javascript">
14512  */
14513
14514
14515
14516 /*
14517  * These classes are derivatives of the similarly named classes in the YUI Library.
14518  * The original license:
14519  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14520  * Code licensed under the BSD License:
14521  * http://developer.yahoo.net/yui/license.txt
14522  */
14523
14524 (function() {
14525
14526 var Event=Roo.EventManager;
14527 var Dom=Roo.lib.Dom;
14528
14529 /**
14530  * @class Roo.dd.DragDrop
14531  * @extends Roo.util.Observable
14532  * Defines the interface and base operation of items that that can be
14533  * dragged or can be drop targets.  It was designed to be extended, overriding
14534  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14535  * Up to three html elements can be associated with a DragDrop instance:
14536  * <ul>
14537  * <li>linked element: the element that is passed into the constructor.
14538  * This is the element which defines the boundaries for interaction with
14539  * other DragDrop objects.</li>
14540  * <li>handle element(s): The drag operation only occurs if the element that
14541  * was clicked matches a handle element.  By default this is the linked
14542  * element, but there are times that you will want only a portion of the
14543  * linked element to initiate the drag operation, and the setHandleElId()
14544  * method provides a way to define this.</li>
14545  * <li>drag element: this represents the element that would be moved along
14546  * with the cursor during a drag operation.  By default, this is the linked
14547  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14548  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14549  * </li>
14550  * </ul>
14551  * This class should not be instantiated until the onload event to ensure that
14552  * the associated elements are available.
14553  * The following would define a DragDrop obj that would interact with any
14554  * other DragDrop obj in the "group1" group:
14555  * <pre>
14556  *  dd = new Roo.dd.DragDrop("div1", "group1");
14557  * </pre>
14558  * Since none of the event handlers have been implemented, nothing would
14559  * actually happen if you were to run the code above.  Normally you would
14560  * override this class or one of the default implementations, but you can
14561  * also override the methods you want on an instance of the class...
14562  * <pre>
14563  *  dd.onDragDrop = function(e, id) {
14564  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14565  *  }
14566  * </pre>
14567  * @constructor
14568  * @param {String} id of the element that is linked to this instance
14569  * @param {String} sGroup the group of related DragDrop objects
14570  * @param {object} config an object containing configurable attributes
14571  *                Valid properties for DragDrop:
14572  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14573  */
14574 Roo.dd.DragDrop = function(id, sGroup, config) {
14575     if (id) {
14576         this.init(id, sGroup, config);
14577     }
14578     
14579 };
14580
14581 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14582
14583     /**
14584      * The id of the element associated with this object.  This is what we
14585      * refer to as the "linked element" because the size and position of
14586      * this element is used to determine when the drag and drop objects have
14587      * interacted.
14588      * @property id
14589      * @type String
14590      */
14591     id: null,
14592
14593     /**
14594      * Configuration attributes passed into the constructor
14595      * @property config
14596      * @type object
14597      */
14598     config: null,
14599
14600     /**
14601      * The id of the element that will be dragged.  By default this is same
14602      * as the linked element , but could be changed to another element. Ex:
14603      * Roo.dd.DDProxy
14604      * @property dragElId
14605      * @type String
14606      * @private
14607      */
14608     dragElId: null,
14609
14610     /**
14611      * the id of the element that initiates the drag operation.  By default
14612      * this is the linked element, but could be changed to be a child of this
14613      * element.  This lets us do things like only starting the drag when the
14614      * header element within the linked html element is clicked.
14615      * @property handleElId
14616      * @type String
14617      * @private
14618      */
14619     handleElId: null,
14620
14621     /**
14622      * An associative array of HTML tags that will be ignored if clicked.
14623      * @property invalidHandleTypes
14624      * @type {string: string}
14625      */
14626     invalidHandleTypes: null,
14627
14628     /**
14629      * An associative array of ids for elements that will be ignored if clicked
14630      * @property invalidHandleIds
14631      * @type {string: string}
14632      */
14633     invalidHandleIds: null,
14634
14635     /**
14636      * An indexted array of css class names for elements that will be ignored
14637      * if clicked.
14638      * @property invalidHandleClasses
14639      * @type string[]
14640      */
14641     invalidHandleClasses: null,
14642
14643     /**
14644      * The linked element's absolute X position at the time the drag was
14645      * started
14646      * @property startPageX
14647      * @type int
14648      * @private
14649      */
14650     startPageX: 0,
14651
14652     /**
14653      * The linked element's absolute X position at the time the drag was
14654      * started
14655      * @property startPageY
14656      * @type int
14657      * @private
14658      */
14659     startPageY: 0,
14660
14661     /**
14662      * The group defines a logical collection of DragDrop objects that are
14663      * related.  Instances only get events when interacting with other
14664      * DragDrop object in the same group.  This lets us define multiple
14665      * groups using a single DragDrop subclass if we want.
14666      * @property groups
14667      * @type {string: string}
14668      */
14669     groups: null,
14670
14671     /**
14672      * Individual drag/drop instances can be locked.  This will prevent
14673      * onmousedown start drag.
14674      * @property locked
14675      * @type boolean
14676      * @private
14677      */
14678     locked: false,
14679
14680     /**
14681      * Lock this instance
14682      * @method lock
14683      */
14684     lock: function() { this.locked = true; },
14685
14686     /**
14687      * Unlock this instace
14688      * @method unlock
14689      */
14690     unlock: function() { this.locked = false; },
14691
14692     /**
14693      * By default, all insances can be a drop target.  This can be disabled by
14694      * setting isTarget to false.
14695      * @method isTarget
14696      * @type boolean
14697      */
14698     isTarget: true,
14699
14700     /**
14701      * The padding configured for this drag and drop object for calculating
14702      * the drop zone intersection with this object.
14703      * @method padding
14704      * @type int[]
14705      */
14706     padding: null,
14707
14708     /**
14709      * Cached reference to the linked element
14710      * @property _domRef
14711      * @private
14712      */
14713     _domRef: null,
14714
14715     /**
14716      * Internal typeof flag
14717      * @property __ygDragDrop
14718      * @private
14719      */
14720     __ygDragDrop: true,
14721
14722     /**
14723      * Set to true when horizontal contraints are applied
14724      * @property constrainX
14725      * @type boolean
14726      * @private
14727      */
14728     constrainX: false,
14729
14730     /**
14731      * Set to true when vertical contraints are applied
14732      * @property constrainY
14733      * @type boolean
14734      * @private
14735      */
14736     constrainY: false,
14737
14738     /**
14739      * The left constraint
14740      * @property minX
14741      * @type int
14742      * @private
14743      */
14744     minX: 0,
14745
14746     /**
14747      * The right constraint
14748      * @property maxX
14749      * @type int
14750      * @private
14751      */
14752     maxX: 0,
14753
14754     /**
14755      * The up constraint
14756      * @property minY
14757      * @type int
14758      * @type int
14759      * @private
14760      */
14761     minY: 0,
14762
14763     /**
14764      * The down constraint
14765      * @property maxY
14766      * @type int
14767      * @private
14768      */
14769     maxY: 0,
14770
14771     /**
14772      * Maintain offsets when we resetconstraints.  Set to true when you want
14773      * the position of the element relative to its parent to stay the same
14774      * when the page changes
14775      *
14776      * @property maintainOffset
14777      * @type boolean
14778      */
14779     maintainOffset: false,
14780
14781     /**
14782      * Array of pixel locations the element will snap to if we specified a
14783      * horizontal graduation/interval.  This array is generated automatically
14784      * when you define a tick interval.
14785      * @property xTicks
14786      * @type int[]
14787      */
14788     xTicks: null,
14789
14790     /**
14791      * Array of pixel locations the element will snap to if we specified a
14792      * vertical graduation/interval.  This array is generated automatically
14793      * when you define a tick interval.
14794      * @property yTicks
14795      * @type int[]
14796      */
14797     yTicks: null,
14798
14799     /**
14800      * By default the drag and drop instance will only respond to the primary
14801      * button click (left button for a right-handed mouse).  Set to true to
14802      * allow drag and drop to start with any mouse click that is propogated
14803      * by the browser
14804      * @property primaryButtonOnly
14805      * @type boolean
14806      */
14807     primaryButtonOnly: true,
14808
14809     /**
14810      * The availabe property is false until the linked dom element is accessible.
14811      * @property available
14812      * @type boolean
14813      */
14814     available: false,
14815
14816     /**
14817      * By default, drags can only be initiated if the mousedown occurs in the
14818      * region the linked element is.  This is done in part to work around a
14819      * bug in some browsers that mis-report the mousedown if the previous
14820      * mouseup happened outside of the window.  This property is set to true
14821      * if outer handles are defined.
14822      *
14823      * @property hasOuterHandles
14824      * @type boolean
14825      * @default false
14826      */
14827     hasOuterHandles: false,
14828
14829     /**
14830      * Code that executes immediately before the startDrag event
14831      * @method b4StartDrag
14832      * @private
14833      */
14834     b4StartDrag: function(x, y) { },
14835
14836     /**
14837      * Abstract method called after a drag/drop object is clicked
14838      * and the drag or mousedown time thresholds have beeen met.
14839      * @method startDrag
14840      * @param {int} X click location
14841      * @param {int} Y click location
14842      */
14843     startDrag: function(x, y) { /* override this */ },
14844
14845     /**
14846      * Code that executes immediately before the onDrag event
14847      * @method b4Drag
14848      * @private
14849      */
14850     b4Drag: function(e) { },
14851
14852     /**
14853      * Abstract method called during the onMouseMove event while dragging an
14854      * object.
14855      * @method onDrag
14856      * @param {Event} e the mousemove event
14857      */
14858     onDrag: function(e) { /* override this */ },
14859
14860     /**
14861      * Abstract method called when this element fist begins hovering over
14862      * another DragDrop obj
14863      * @method onDragEnter
14864      * @param {Event} e the mousemove event
14865      * @param {String|DragDrop[]} id In POINT mode, the element
14866      * id this is hovering over.  In INTERSECT mode, an array of one or more
14867      * dragdrop items being hovered over.
14868      */
14869     onDragEnter: function(e, id) { /* override this */ },
14870
14871     /**
14872      * Code that executes immediately before the onDragOver event
14873      * @method b4DragOver
14874      * @private
14875      */
14876     b4DragOver: function(e) { },
14877
14878     /**
14879      * Abstract method called when this element is hovering over another
14880      * DragDrop obj
14881      * @method onDragOver
14882      * @param {Event} e the mousemove event
14883      * @param {String|DragDrop[]} id In POINT mode, the element
14884      * id this is hovering over.  In INTERSECT mode, an array of dd items
14885      * being hovered over.
14886      */
14887     onDragOver: function(e, id) { /* override this */ },
14888
14889     /**
14890      * Code that executes immediately before the onDragOut event
14891      * @method b4DragOut
14892      * @private
14893      */
14894     b4DragOut: function(e) { },
14895
14896     /**
14897      * Abstract method called when we are no longer hovering over an element
14898      * @method onDragOut
14899      * @param {Event} e the mousemove event
14900      * @param {String|DragDrop[]} id In POINT mode, the element
14901      * id this was hovering over.  In INTERSECT mode, an array of dd items
14902      * that the mouse is no longer over.
14903      */
14904     onDragOut: function(e, id) { /* override this */ },
14905
14906     /**
14907      * Code that executes immediately before the onDragDrop event
14908      * @method b4DragDrop
14909      * @private
14910      */
14911     b4DragDrop: function(e) { },
14912
14913     /**
14914      * Abstract method called when this item is dropped on another DragDrop
14915      * obj
14916      * @method onDragDrop
14917      * @param {Event} e the mouseup event
14918      * @param {String|DragDrop[]} id In POINT mode, the element
14919      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14920      * was dropped on.
14921      */
14922     onDragDrop: function(e, id) { /* override this */ },
14923
14924     /**
14925      * Abstract method called when this item is dropped on an area with no
14926      * drop target
14927      * @method onInvalidDrop
14928      * @param {Event} e the mouseup event
14929      */
14930     onInvalidDrop: function(e) { /* override this */ },
14931
14932     /**
14933      * Code that executes immediately before the endDrag event
14934      * @method b4EndDrag
14935      * @private
14936      */
14937     b4EndDrag: function(e) { },
14938
14939     /**
14940      * Fired when we are done dragging the object
14941      * @method endDrag
14942      * @param {Event} e the mouseup event
14943      */
14944     endDrag: function(e) { /* override this */ },
14945
14946     /**
14947      * Code executed immediately before the onMouseDown event
14948      * @method b4MouseDown
14949      * @param {Event} e the mousedown event
14950      * @private
14951      */
14952     b4MouseDown: function(e) {  },
14953
14954     /**
14955      * Event handler that fires when a drag/drop obj gets a mousedown
14956      * @method onMouseDown
14957      * @param {Event} e the mousedown event
14958      */
14959     onMouseDown: function(e) { /* override this */ },
14960
14961     /**
14962      * Event handler that fires when a drag/drop obj gets a mouseup
14963      * @method onMouseUp
14964      * @param {Event} e the mouseup event
14965      */
14966     onMouseUp: function(e) { /* override this */ },
14967
14968     /**
14969      * Override the onAvailable method to do what is needed after the initial
14970      * position was determined.
14971      * @method onAvailable
14972      */
14973     onAvailable: function () {
14974     },
14975
14976     /*
14977      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14978      * @type Object
14979      */
14980     defaultPadding : {left:0, right:0, top:0, bottom:0},
14981
14982     /*
14983      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14984  *
14985  * Usage:
14986  <pre><code>
14987  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14988                 { dragElId: "existingProxyDiv" });
14989  dd.startDrag = function(){
14990      this.constrainTo("parent-id");
14991  };
14992  </code></pre>
14993  * Or you can initalize it using the {@link Roo.Element} object:
14994  <pre><code>
14995  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14996      startDrag : function(){
14997          this.constrainTo("parent-id");
14998      }
14999  });
15000  </code></pre>
15001      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15002      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15003      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15004      * an object containing the sides to pad. For example: {right:10, bottom:10}
15005      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15006      */
15007     constrainTo : function(constrainTo, pad, inContent){
15008         if(typeof pad == "number"){
15009             pad = {left: pad, right:pad, top:pad, bottom:pad};
15010         }
15011         pad = pad || this.defaultPadding;
15012         var b = Roo.get(this.getEl()).getBox();
15013         var ce = Roo.get(constrainTo);
15014         var s = ce.getScroll();
15015         var c, cd = ce.dom;
15016         if(cd == document.body){
15017             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15018         }else{
15019             xy = ce.getXY();
15020             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15021         }
15022
15023
15024         var topSpace = b.y - c.y;
15025         var leftSpace = b.x - c.x;
15026
15027         this.resetConstraints();
15028         this.setXConstraint(leftSpace - (pad.left||0), // left
15029                 c.width - leftSpace - b.width - (pad.right||0) //right
15030         );
15031         this.setYConstraint(topSpace - (pad.top||0), //top
15032                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15033         );
15034     },
15035
15036     /**
15037      * Returns a reference to the linked element
15038      * @method getEl
15039      * @return {HTMLElement} the html element
15040      */
15041     getEl: function() {
15042         if (!this._domRef) {
15043             this._domRef = Roo.getDom(this.id);
15044         }
15045
15046         return this._domRef;
15047     },
15048
15049     /**
15050      * Returns a reference to the actual element to drag.  By default this is
15051      * the same as the html element, but it can be assigned to another
15052      * element. An example of this can be found in Roo.dd.DDProxy
15053      * @method getDragEl
15054      * @return {HTMLElement} the html element
15055      */
15056     getDragEl: function() {
15057         return Roo.getDom(this.dragElId);
15058     },
15059
15060     /**
15061      * Sets up the DragDrop object.  Must be called in the constructor of any
15062      * Roo.dd.DragDrop subclass
15063      * @method init
15064      * @param id the id of the linked element
15065      * @param {String} sGroup the group of related items
15066      * @param {object} config configuration attributes
15067      */
15068     init: function(id, sGroup, config) {
15069         this.initTarget(id, sGroup, config);
15070         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15071         // Event.on(this.id, "selectstart", Event.preventDefault);
15072     },
15073
15074     /**
15075      * Initializes Targeting functionality only... the object does not
15076      * get a mousedown handler.
15077      * @method initTarget
15078      * @param id the id of the linked element
15079      * @param {String} sGroup the group of related items
15080      * @param {object} config configuration attributes
15081      */
15082     initTarget: function(id, sGroup, config) {
15083
15084         // configuration attributes
15085         this.config = config || {};
15086
15087         // create a local reference to the drag and drop manager
15088         this.DDM = Roo.dd.DDM;
15089         // initialize the groups array
15090         this.groups = {};
15091
15092         // assume that we have an element reference instead of an id if the
15093         // parameter is not a string
15094         if (typeof id !== "string") {
15095             id = Roo.id(id);
15096         }
15097
15098         // set the id
15099         this.id = id;
15100
15101         // add to an interaction group
15102         this.addToGroup((sGroup) ? sGroup : "default");
15103
15104         // We don't want to register this as the handle with the manager
15105         // so we just set the id rather than calling the setter.
15106         this.handleElId = id;
15107
15108         // the linked element is the element that gets dragged by default
15109         this.setDragElId(id);
15110
15111         // by default, clicked anchors will not start drag operations.
15112         this.invalidHandleTypes = { A: "A" };
15113         this.invalidHandleIds = {};
15114         this.invalidHandleClasses = [];
15115
15116         this.applyConfig();
15117
15118         this.handleOnAvailable();
15119     },
15120
15121     /**
15122      * Applies the configuration parameters that were passed into the constructor.
15123      * This is supposed to happen at each level through the inheritance chain.  So
15124      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15125      * DragDrop in order to get all of the parameters that are available in
15126      * each object.
15127      * @method applyConfig
15128      */
15129     applyConfig: function() {
15130
15131         // configurable properties:
15132         //    padding, isTarget, maintainOffset, primaryButtonOnly
15133         this.padding           = this.config.padding || [0, 0, 0, 0];
15134         this.isTarget          = (this.config.isTarget !== false);
15135         this.maintainOffset    = (this.config.maintainOffset);
15136         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15137
15138     },
15139
15140     /**
15141      * Executed when the linked element is available
15142      * @method handleOnAvailable
15143      * @private
15144      */
15145     handleOnAvailable: function() {
15146         this.available = true;
15147         this.resetConstraints();
15148         this.onAvailable();
15149     },
15150
15151      /**
15152      * Configures the padding for the target zone in px.  Effectively expands
15153      * (or reduces) the virtual object size for targeting calculations.
15154      * Supports css-style shorthand; if only one parameter is passed, all sides
15155      * will have that padding, and if only two are passed, the top and bottom
15156      * will have the first param, the left and right the second.
15157      * @method setPadding
15158      * @param {int} iTop    Top pad
15159      * @param {int} iRight  Right pad
15160      * @param {int} iBot    Bot pad
15161      * @param {int} iLeft   Left pad
15162      */
15163     setPadding: function(iTop, iRight, iBot, iLeft) {
15164         // this.padding = [iLeft, iRight, iTop, iBot];
15165         if (!iRight && 0 !== iRight) {
15166             this.padding = [iTop, iTop, iTop, iTop];
15167         } else if (!iBot && 0 !== iBot) {
15168             this.padding = [iTop, iRight, iTop, iRight];
15169         } else {
15170             this.padding = [iTop, iRight, iBot, iLeft];
15171         }
15172     },
15173
15174     /**
15175      * Stores the initial placement of the linked element.
15176      * @method setInitialPosition
15177      * @param {int} diffX   the X offset, default 0
15178      * @param {int} diffY   the Y offset, default 0
15179      */
15180     setInitPosition: function(diffX, diffY) {
15181         var el = this.getEl();
15182
15183         if (!this.DDM.verifyEl(el)) {
15184             return;
15185         }
15186
15187         var dx = diffX || 0;
15188         var dy = diffY || 0;
15189
15190         var p = Dom.getXY( el );
15191
15192         this.initPageX = p[0] - dx;
15193         this.initPageY = p[1] - dy;
15194
15195         this.lastPageX = p[0];
15196         this.lastPageY = p[1];
15197
15198
15199         this.setStartPosition(p);
15200     },
15201
15202     /**
15203      * Sets the start position of the element.  This is set when the obj
15204      * is initialized, the reset when a drag is started.
15205      * @method setStartPosition
15206      * @param pos current position (from previous lookup)
15207      * @private
15208      */
15209     setStartPosition: function(pos) {
15210         var p = pos || Dom.getXY( this.getEl() );
15211         this.deltaSetXY = null;
15212
15213         this.startPageX = p[0];
15214         this.startPageY = p[1];
15215     },
15216
15217     /**
15218      * Add this instance to a group of related drag/drop objects.  All
15219      * instances belong to at least one group, and can belong to as many
15220      * groups as needed.
15221      * @method addToGroup
15222      * @param sGroup {string} the name of the group
15223      */
15224     addToGroup: function(sGroup) {
15225         this.groups[sGroup] = true;
15226         this.DDM.regDragDrop(this, sGroup);
15227     },
15228
15229     /**
15230      * Remove's this instance from the supplied interaction group
15231      * @method removeFromGroup
15232      * @param {string}  sGroup  The group to drop
15233      */
15234     removeFromGroup: function(sGroup) {
15235         if (this.groups[sGroup]) {
15236             delete this.groups[sGroup];
15237         }
15238
15239         this.DDM.removeDDFromGroup(this, sGroup);
15240     },
15241
15242     /**
15243      * Allows you to specify that an element other than the linked element
15244      * will be moved with the cursor during a drag
15245      * @method setDragElId
15246      * @param id {string} the id of the element that will be used to initiate the drag
15247      */
15248     setDragElId: function(id) {
15249         this.dragElId = id;
15250     },
15251
15252     /**
15253      * Allows you to specify a child of the linked element that should be
15254      * used to initiate the drag operation.  An example of this would be if
15255      * you have a content div with text and links.  Clicking anywhere in the
15256      * content area would normally start the drag operation.  Use this method
15257      * to specify that an element inside of the content div is the element
15258      * that starts the drag operation.
15259      * @method setHandleElId
15260      * @param id {string} the id of the element that will be used to
15261      * initiate the drag.
15262      */
15263     setHandleElId: function(id) {
15264         if (typeof id !== "string") {
15265             id = Roo.id(id);
15266         }
15267         this.handleElId = id;
15268         this.DDM.regHandle(this.id, id);
15269     },
15270
15271     /**
15272      * Allows you to set an element outside of the linked element as a drag
15273      * handle
15274      * @method setOuterHandleElId
15275      * @param id the id of the element that will be used to initiate the drag
15276      */
15277     setOuterHandleElId: function(id) {
15278         if (typeof id !== "string") {
15279             id = Roo.id(id);
15280         }
15281         Event.on(id, "mousedown",
15282                 this.handleMouseDown, this);
15283         this.setHandleElId(id);
15284
15285         this.hasOuterHandles = true;
15286     },
15287
15288     /**
15289      * Remove all drag and drop hooks for this element
15290      * @method unreg
15291      */
15292     unreg: function() {
15293         Event.un(this.id, "mousedown",
15294                 this.handleMouseDown);
15295         this._domRef = null;
15296         this.DDM._remove(this);
15297     },
15298
15299     destroy : function(){
15300         this.unreg();
15301     },
15302
15303     /**
15304      * Returns true if this instance is locked, or the drag drop mgr is locked
15305      * (meaning that all drag/drop is disabled on the page.)
15306      * @method isLocked
15307      * @return {boolean} true if this obj or all drag/drop is locked, else
15308      * false
15309      */
15310     isLocked: function() {
15311         return (this.DDM.isLocked() || this.locked);
15312     },
15313
15314     /**
15315      * Fired when this object is clicked
15316      * @method handleMouseDown
15317      * @param {Event} e
15318      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15319      * @private
15320      */
15321     handleMouseDown: function(e, oDD){
15322         if (this.primaryButtonOnly && e.button != 0) {
15323             return;
15324         }
15325
15326         if (this.isLocked()) {
15327             return;
15328         }
15329
15330         this.DDM.refreshCache(this.groups);
15331
15332         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15333         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15334         } else {
15335             if (this.clickValidator(e)) {
15336
15337                 // set the initial element position
15338                 this.setStartPosition();
15339
15340
15341                 this.b4MouseDown(e);
15342                 this.onMouseDown(e);
15343
15344                 this.DDM.handleMouseDown(e, this);
15345
15346                 this.DDM.stopEvent(e);
15347             } else {
15348
15349
15350             }
15351         }
15352     },
15353
15354     clickValidator: function(e) {
15355         var target = e.getTarget();
15356         return ( this.isValidHandleChild(target) &&
15357                     (this.id == this.handleElId ||
15358                         this.DDM.handleWasClicked(target, this.id)) );
15359     },
15360
15361     /**
15362      * Allows you to specify a tag name that should not start a drag operation
15363      * when clicked.  This is designed to facilitate embedding links within a
15364      * drag handle that do something other than start the drag.
15365      * @method addInvalidHandleType
15366      * @param {string} tagName the type of element to exclude
15367      */
15368     addInvalidHandleType: function(tagName) {
15369         var type = tagName.toUpperCase();
15370         this.invalidHandleTypes[type] = type;
15371     },
15372
15373     /**
15374      * Lets you to specify an element id for a child of a drag handle
15375      * that should not initiate a drag
15376      * @method addInvalidHandleId
15377      * @param {string} id the element id of the element you wish to ignore
15378      */
15379     addInvalidHandleId: function(id) {
15380         if (typeof id !== "string") {
15381             id = Roo.id(id);
15382         }
15383         this.invalidHandleIds[id] = id;
15384     },
15385
15386     /**
15387      * Lets you specify a css class of elements that will not initiate a drag
15388      * @method addInvalidHandleClass
15389      * @param {string} cssClass the class of the elements you wish to ignore
15390      */
15391     addInvalidHandleClass: function(cssClass) {
15392         this.invalidHandleClasses.push(cssClass);
15393     },
15394
15395     /**
15396      * Unsets an excluded tag name set by addInvalidHandleType
15397      * @method removeInvalidHandleType
15398      * @param {string} tagName the type of element to unexclude
15399      */
15400     removeInvalidHandleType: function(tagName) {
15401         var type = tagName.toUpperCase();
15402         // this.invalidHandleTypes[type] = null;
15403         delete this.invalidHandleTypes[type];
15404     },
15405
15406     /**
15407      * Unsets an invalid handle id
15408      * @method removeInvalidHandleId
15409      * @param {string} id the id of the element to re-enable
15410      */
15411     removeInvalidHandleId: function(id) {
15412         if (typeof id !== "string") {
15413             id = Roo.id(id);
15414         }
15415         delete this.invalidHandleIds[id];
15416     },
15417
15418     /**
15419      * Unsets an invalid css class
15420      * @method removeInvalidHandleClass
15421      * @param {string} cssClass the class of the element(s) you wish to
15422      * re-enable
15423      */
15424     removeInvalidHandleClass: function(cssClass) {
15425         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15426             if (this.invalidHandleClasses[i] == cssClass) {
15427                 delete this.invalidHandleClasses[i];
15428             }
15429         }
15430     },
15431
15432     /**
15433      * Checks the tag exclusion list to see if this click should be ignored
15434      * @method isValidHandleChild
15435      * @param {HTMLElement} node the HTMLElement to evaluate
15436      * @return {boolean} true if this is a valid tag type, false if not
15437      */
15438     isValidHandleChild: function(node) {
15439
15440         var valid = true;
15441         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15442         var nodeName;
15443         try {
15444             nodeName = node.nodeName.toUpperCase();
15445         } catch(e) {
15446             nodeName = node.nodeName;
15447         }
15448         valid = valid && !this.invalidHandleTypes[nodeName];
15449         valid = valid && !this.invalidHandleIds[node.id];
15450
15451         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15452             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15453         }
15454
15455
15456         return valid;
15457
15458     },
15459
15460     /**
15461      * Create the array of horizontal tick marks if an interval was specified
15462      * in setXConstraint().
15463      * @method setXTicks
15464      * @private
15465      */
15466     setXTicks: function(iStartX, iTickSize) {
15467         this.xTicks = [];
15468         this.xTickSize = iTickSize;
15469
15470         var tickMap = {};
15471
15472         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15473             if (!tickMap[i]) {
15474                 this.xTicks[this.xTicks.length] = i;
15475                 tickMap[i] = true;
15476             }
15477         }
15478
15479         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15480             if (!tickMap[i]) {
15481                 this.xTicks[this.xTicks.length] = i;
15482                 tickMap[i] = true;
15483             }
15484         }
15485
15486         this.xTicks.sort(this.DDM.numericSort) ;
15487     },
15488
15489     /**
15490      * Create the array of vertical tick marks if an interval was specified in
15491      * setYConstraint().
15492      * @method setYTicks
15493      * @private
15494      */
15495     setYTicks: function(iStartY, iTickSize) {
15496         this.yTicks = [];
15497         this.yTickSize = iTickSize;
15498
15499         var tickMap = {};
15500
15501         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15502             if (!tickMap[i]) {
15503                 this.yTicks[this.yTicks.length] = i;
15504                 tickMap[i] = true;
15505             }
15506         }
15507
15508         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15509             if (!tickMap[i]) {
15510                 this.yTicks[this.yTicks.length] = i;
15511                 tickMap[i] = true;
15512             }
15513         }
15514
15515         this.yTicks.sort(this.DDM.numericSort) ;
15516     },
15517
15518     /**
15519      * By default, the element can be dragged any place on the screen.  Use
15520      * this method to limit the horizontal travel of the element.  Pass in
15521      * 0,0 for the parameters if you want to lock the drag to the y axis.
15522      * @method setXConstraint
15523      * @param {int} iLeft the number of pixels the element can move to the left
15524      * @param {int} iRight the number of pixels the element can move to the
15525      * right
15526      * @param {int} iTickSize optional parameter for specifying that the
15527      * element
15528      * should move iTickSize pixels at a time.
15529      */
15530     setXConstraint: function(iLeft, iRight, iTickSize) {
15531         this.leftConstraint = iLeft;
15532         this.rightConstraint = iRight;
15533
15534         this.minX = this.initPageX - iLeft;
15535         this.maxX = this.initPageX + iRight;
15536         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15537
15538         this.constrainX = true;
15539     },
15540
15541     /**
15542      * Clears any constraints applied to this instance.  Also clears ticks
15543      * since they can't exist independent of a constraint at this time.
15544      * @method clearConstraints
15545      */
15546     clearConstraints: function() {
15547         this.constrainX = false;
15548         this.constrainY = false;
15549         this.clearTicks();
15550     },
15551
15552     /**
15553      * Clears any tick interval defined for this instance
15554      * @method clearTicks
15555      */
15556     clearTicks: function() {
15557         this.xTicks = null;
15558         this.yTicks = null;
15559         this.xTickSize = 0;
15560         this.yTickSize = 0;
15561     },
15562
15563     /**
15564      * By default, the element can be dragged any place on the screen.  Set
15565      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15566      * parameters if you want to lock the drag to the x axis.
15567      * @method setYConstraint
15568      * @param {int} iUp the number of pixels the element can move up
15569      * @param {int} iDown the number of pixels the element can move down
15570      * @param {int} iTickSize optional parameter for specifying that the
15571      * element should move iTickSize pixels at a time.
15572      */
15573     setYConstraint: function(iUp, iDown, iTickSize) {
15574         this.topConstraint = iUp;
15575         this.bottomConstraint = iDown;
15576
15577         this.minY = this.initPageY - iUp;
15578         this.maxY = this.initPageY + iDown;
15579         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15580
15581         this.constrainY = true;
15582
15583     },
15584
15585     /**
15586      * resetConstraints must be called if you manually reposition a dd element.
15587      * @method resetConstraints
15588      * @param {boolean} maintainOffset
15589      */
15590     resetConstraints: function() {
15591
15592
15593         // Maintain offsets if necessary
15594         if (this.initPageX || this.initPageX === 0) {
15595             // figure out how much this thing has moved
15596             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15597             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15598
15599             this.setInitPosition(dx, dy);
15600
15601         // This is the first time we have detected the element's position
15602         } else {
15603             this.setInitPosition();
15604         }
15605
15606         if (this.constrainX) {
15607             this.setXConstraint( this.leftConstraint,
15608                                  this.rightConstraint,
15609                                  this.xTickSize        );
15610         }
15611
15612         if (this.constrainY) {
15613             this.setYConstraint( this.topConstraint,
15614                                  this.bottomConstraint,
15615                                  this.yTickSize         );
15616         }
15617     },
15618
15619     /**
15620      * Normally the drag element is moved pixel by pixel, but we can specify
15621      * that it move a number of pixels at a time.  This method resolves the
15622      * location when we have it set up like this.
15623      * @method getTick
15624      * @param {int} val where we want to place the object
15625      * @param {int[]} tickArray sorted array of valid points
15626      * @return {int} the closest tick
15627      * @private
15628      */
15629     getTick: function(val, tickArray) {
15630
15631         if (!tickArray) {
15632             // If tick interval is not defined, it is effectively 1 pixel,
15633             // so we return the value passed to us.
15634             return val;
15635         } else if (tickArray[0] >= val) {
15636             // The value is lower than the first tick, so we return the first
15637             // tick.
15638             return tickArray[0];
15639         } else {
15640             for (var i=0, len=tickArray.length; i<len; ++i) {
15641                 var next = i + 1;
15642                 if (tickArray[next] && tickArray[next] >= val) {
15643                     var diff1 = val - tickArray[i];
15644                     var diff2 = tickArray[next] - val;
15645                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15646                 }
15647             }
15648
15649             // The value is larger than the last tick, so we return the last
15650             // tick.
15651             return tickArray[tickArray.length - 1];
15652         }
15653     },
15654
15655     /**
15656      * toString method
15657      * @method toString
15658      * @return {string} string representation of the dd obj
15659      */
15660     toString: function() {
15661         return ("DragDrop " + this.id);
15662     }
15663
15664 });
15665
15666 })();
15667 /*
15668  * Based on:
15669  * Ext JS Library 1.1.1
15670  * Copyright(c) 2006-2007, Ext JS, LLC.
15671  *
15672  * Originally Released Under LGPL - original licence link has changed is not relivant.
15673  *
15674  * Fork - LGPL
15675  * <script type="text/javascript">
15676  */
15677
15678
15679 /**
15680  * The drag and drop utility provides a framework for building drag and drop
15681  * applications.  In addition to enabling drag and drop for specific elements,
15682  * the drag and drop elements are tracked by the manager class, and the
15683  * interactions between the various elements are tracked during the drag and
15684  * the implementing code is notified about these important moments.
15685  */
15686
15687 // Only load the library once.  Rewriting the manager class would orphan
15688 // existing drag and drop instances.
15689 if (!Roo.dd.DragDropMgr) {
15690
15691 /**
15692  * @class Roo.dd.DragDropMgr
15693  * DragDropMgr is a singleton that tracks the element interaction for
15694  * all DragDrop items in the window.  Generally, you will not call
15695  * this class directly, but it does have helper methods that could
15696  * be useful in your DragDrop implementations.
15697  * @singleton
15698  */
15699 Roo.dd.DragDropMgr = function() {
15700
15701     var Event = Roo.EventManager;
15702
15703     return {
15704
15705         /**
15706          * Two dimensional Array of registered DragDrop objects.  The first
15707          * dimension is the DragDrop item group, the second the DragDrop
15708          * object.
15709          * @property ids
15710          * @type {string: string}
15711          * @private
15712          * @static
15713          */
15714         ids: {},
15715
15716         /**
15717          * Array of element ids defined as drag handles.  Used to determine
15718          * if the element that generated the mousedown event is actually the
15719          * handle and not the html element itself.
15720          * @property handleIds
15721          * @type {string: string}
15722          * @private
15723          * @static
15724          */
15725         handleIds: {},
15726
15727         /**
15728          * the DragDrop object that is currently being dragged
15729          * @property dragCurrent
15730          * @type DragDrop
15731          * @private
15732          * @static
15733          **/
15734         dragCurrent: null,
15735
15736         /**
15737          * the DragDrop object(s) that are being hovered over
15738          * @property dragOvers
15739          * @type Array
15740          * @private
15741          * @static
15742          */
15743         dragOvers: {},
15744
15745         /**
15746          * the X distance between the cursor and the object being dragged
15747          * @property deltaX
15748          * @type int
15749          * @private
15750          * @static
15751          */
15752         deltaX: 0,
15753
15754         /**
15755          * the Y distance between the cursor and the object being dragged
15756          * @property deltaY
15757          * @type int
15758          * @private
15759          * @static
15760          */
15761         deltaY: 0,
15762
15763         /**
15764          * Flag to determine if we should prevent the default behavior of the
15765          * events we define. By default this is true, but this can be set to
15766          * false if you need the default behavior (not recommended)
15767          * @property preventDefault
15768          * @type boolean
15769          * @static
15770          */
15771         preventDefault: true,
15772
15773         /**
15774          * Flag to determine if we should stop the propagation of the events
15775          * we generate. This is true by default but you may want to set it to
15776          * false if the html element contains other features that require the
15777          * mouse click.
15778          * @property stopPropagation
15779          * @type boolean
15780          * @static
15781          */
15782         stopPropagation: true,
15783
15784         /**
15785          * Internal flag that is set to true when drag and drop has been
15786          * intialized
15787          * @property initialized
15788          * @private
15789          * @static
15790          */
15791         initalized: false,
15792
15793         /**
15794          * All drag and drop can be disabled.
15795          * @property locked
15796          * @private
15797          * @static
15798          */
15799         locked: false,
15800
15801         /**
15802          * Called the first time an element is registered.
15803          * @method init
15804          * @private
15805          * @static
15806          */
15807         init: function() {
15808             this.initialized = true;
15809         },
15810
15811         /**
15812          * In point mode, drag and drop interaction is defined by the
15813          * location of the cursor during the drag/drop
15814          * @property POINT
15815          * @type int
15816          * @static
15817          */
15818         POINT: 0,
15819
15820         /**
15821          * In intersect mode, drag and drop interactio nis defined by the
15822          * overlap of two or more drag and drop objects.
15823          * @property INTERSECT
15824          * @type int
15825          * @static
15826          */
15827         INTERSECT: 1,
15828
15829         /**
15830          * The current drag and drop mode.  Default: POINT
15831          * @property mode
15832          * @type int
15833          * @static
15834          */
15835         mode: 0,
15836
15837         /**
15838          * Runs method on all drag and drop objects
15839          * @method _execOnAll
15840          * @private
15841          * @static
15842          */
15843         _execOnAll: function(sMethod, args) {
15844             for (var i in this.ids) {
15845                 for (var j in this.ids[i]) {
15846                     var oDD = this.ids[i][j];
15847                     if (! this.isTypeOfDD(oDD)) {
15848                         continue;
15849                     }
15850                     oDD[sMethod].apply(oDD, args);
15851                 }
15852             }
15853         },
15854
15855         /**
15856          * Drag and drop initialization.  Sets up the global event handlers
15857          * @method _onLoad
15858          * @private
15859          * @static
15860          */
15861         _onLoad: function() {
15862
15863             this.init();
15864
15865
15866             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15867             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15868             Event.on(window,   "unload",    this._onUnload, this, true);
15869             Event.on(window,   "resize",    this._onResize, this, true);
15870             // Event.on(window,   "mouseout",    this._test);
15871
15872         },
15873
15874         /**
15875          * Reset constraints on all drag and drop objs
15876          * @method _onResize
15877          * @private
15878          * @static
15879          */
15880         _onResize: function(e) {
15881             this._execOnAll("resetConstraints", []);
15882         },
15883
15884         /**
15885          * Lock all drag and drop functionality
15886          * @method lock
15887          * @static
15888          */
15889         lock: function() { this.locked = true; },
15890
15891         /**
15892          * Unlock all drag and drop functionality
15893          * @method unlock
15894          * @static
15895          */
15896         unlock: function() { this.locked = false; },
15897
15898         /**
15899          * Is drag and drop locked?
15900          * @method isLocked
15901          * @return {boolean} True if drag and drop is locked, false otherwise.
15902          * @static
15903          */
15904         isLocked: function() { return this.locked; },
15905
15906         /**
15907          * Location cache that is set for all drag drop objects when a drag is
15908          * initiated, cleared when the drag is finished.
15909          * @property locationCache
15910          * @private
15911          * @static
15912          */
15913         locationCache: {},
15914
15915         /**
15916          * Set useCache to false if you want to force object the lookup of each
15917          * drag and drop linked element constantly during a drag.
15918          * @property useCache
15919          * @type boolean
15920          * @static
15921          */
15922         useCache: true,
15923
15924         /**
15925          * The number of pixels that the mouse needs to move after the
15926          * mousedown before the drag is initiated.  Default=3;
15927          * @property clickPixelThresh
15928          * @type int
15929          * @static
15930          */
15931         clickPixelThresh: 3,
15932
15933         /**
15934          * The number of milliseconds after the mousedown event to initiate the
15935          * drag if we don't get a mouseup event. Default=1000
15936          * @property clickTimeThresh
15937          * @type int
15938          * @static
15939          */
15940         clickTimeThresh: 350,
15941
15942         /**
15943          * Flag that indicates that either the drag pixel threshold or the
15944          * mousdown time threshold has been met
15945          * @property dragThreshMet
15946          * @type boolean
15947          * @private
15948          * @static
15949          */
15950         dragThreshMet: false,
15951
15952         /**
15953          * Timeout used for the click time threshold
15954          * @property clickTimeout
15955          * @type Object
15956          * @private
15957          * @static
15958          */
15959         clickTimeout: null,
15960
15961         /**
15962          * The X position of the mousedown event stored for later use when a
15963          * drag threshold is met.
15964          * @property startX
15965          * @type int
15966          * @private
15967          * @static
15968          */
15969         startX: 0,
15970
15971         /**
15972          * The Y position of the mousedown event stored for later use when a
15973          * drag threshold is met.
15974          * @property startY
15975          * @type int
15976          * @private
15977          * @static
15978          */
15979         startY: 0,
15980
15981         /**
15982          * Each DragDrop instance must be registered with the DragDropMgr.
15983          * This is executed in DragDrop.init()
15984          * @method regDragDrop
15985          * @param {DragDrop} oDD the DragDrop object to register
15986          * @param {String} sGroup the name of the group this element belongs to
15987          * @static
15988          */
15989         regDragDrop: function(oDD, sGroup) {
15990             if (!this.initialized) { this.init(); }
15991
15992             if (!this.ids[sGroup]) {
15993                 this.ids[sGroup] = {};
15994             }
15995             this.ids[sGroup][oDD.id] = oDD;
15996         },
15997
15998         /**
15999          * Removes the supplied dd instance from the supplied group. Executed
16000          * by DragDrop.removeFromGroup, so don't call this function directly.
16001          * @method removeDDFromGroup
16002          * @private
16003          * @static
16004          */
16005         removeDDFromGroup: function(oDD, sGroup) {
16006             if (!this.ids[sGroup]) {
16007                 this.ids[sGroup] = {};
16008             }
16009
16010             var obj = this.ids[sGroup];
16011             if (obj && obj[oDD.id]) {
16012                 delete obj[oDD.id];
16013             }
16014         },
16015
16016         /**
16017          * Unregisters a drag and drop item.  This is executed in
16018          * DragDrop.unreg, use that method instead of calling this directly.
16019          * @method _remove
16020          * @private
16021          * @static
16022          */
16023         _remove: function(oDD) {
16024             for (var g in oDD.groups) {
16025                 if (g && this.ids[g][oDD.id]) {
16026                     delete this.ids[g][oDD.id];
16027                 }
16028             }
16029             delete this.handleIds[oDD.id];
16030         },
16031
16032         /**
16033          * Each DragDrop handle element must be registered.  This is done
16034          * automatically when executing DragDrop.setHandleElId()
16035          * @method regHandle
16036          * @param {String} sDDId the DragDrop id this element is a handle for
16037          * @param {String} sHandleId the id of the element that is the drag
16038          * handle
16039          * @static
16040          */
16041         regHandle: function(sDDId, sHandleId) {
16042             if (!this.handleIds[sDDId]) {
16043                 this.handleIds[sDDId] = {};
16044             }
16045             this.handleIds[sDDId][sHandleId] = sHandleId;
16046         },
16047
16048         /**
16049          * Utility function to determine if a given element has been
16050          * registered as a drag drop item.
16051          * @method isDragDrop
16052          * @param {String} id the element id to check
16053          * @return {boolean} true if this element is a DragDrop item,
16054          * false otherwise
16055          * @static
16056          */
16057         isDragDrop: function(id) {
16058             return ( this.getDDById(id) ) ? true : false;
16059         },
16060
16061         /**
16062          * Returns the drag and drop instances that are in all groups the
16063          * passed in instance belongs to.
16064          * @method getRelated
16065          * @param {DragDrop} p_oDD the obj to get related data for
16066          * @param {boolean} bTargetsOnly if true, only return targetable objs
16067          * @return {DragDrop[]} the related instances
16068          * @static
16069          */
16070         getRelated: function(p_oDD, bTargetsOnly) {
16071             var oDDs = [];
16072             for (var i in p_oDD.groups) {
16073                 for (j in this.ids[i]) {
16074                     var dd = this.ids[i][j];
16075                     if (! this.isTypeOfDD(dd)) {
16076                         continue;
16077                     }
16078                     if (!bTargetsOnly || dd.isTarget) {
16079                         oDDs[oDDs.length] = dd;
16080                     }
16081                 }
16082             }
16083
16084             return oDDs;
16085         },
16086
16087         /**
16088          * Returns true if the specified dd target is a legal target for
16089          * the specifice drag obj
16090          * @method isLegalTarget
16091          * @param {DragDrop} the drag obj
16092          * @param {DragDrop} the target
16093          * @return {boolean} true if the target is a legal target for the
16094          * dd obj
16095          * @static
16096          */
16097         isLegalTarget: function (oDD, oTargetDD) {
16098             var targets = this.getRelated(oDD, true);
16099             for (var i=0, len=targets.length;i<len;++i) {
16100                 if (targets[i].id == oTargetDD.id) {
16101                     return true;
16102                 }
16103             }
16104
16105             return false;
16106         },
16107
16108         /**
16109          * My goal is to be able to transparently determine if an object is
16110          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16111          * returns "object", oDD.constructor.toString() always returns
16112          * "DragDrop" and not the name of the subclass.  So for now it just
16113          * evaluates a well-known variable in DragDrop.
16114          * @method isTypeOfDD
16115          * @param {Object} the object to evaluate
16116          * @return {boolean} true if typeof oDD = DragDrop
16117          * @static
16118          */
16119         isTypeOfDD: function (oDD) {
16120             return (oDD && oDD.__ygDragDrop);
16121         },
16122
16123         /**
16124          * Utility function to determine if a given element has been
16125          * registered as a drag drop handle for the given Drag Drop object.
16126          * @method isHandle
16127          * @param {String} id the element id to check
16128          * @return {boolean} true if this element is a DragDrop handle, false
16129          * otherwise
16130          * @static
16131          */
16132         isHandle: function(sDDId, sHandleId) {
16133             return ( this.handleIds[sDDId] &&
16134                             this.handleIds[sDDId][sHandleId] );
16135         },
16136
16137         /**
16138          * Returns the DragDrop instance for a given id
16139          * @method getDDById
16140          * @param {String} id the id of the DragDrop object
16141          * @return {DragDrop} the drag drop object, null if it is not found
16142          * @static
16143          */
16144         getDDById: function(id) {
16145             for (var i in this.ids) {
16146                 if (this.ids[i][id]) {
16147                     return this.ids[i][id];
16148                 }
16149             }
16150             return null;
16151         },
16152
16153         /**
16154          * Fired after a registered DragDrop object gets the mousedown event.
16155          * Sets up the events required to track the object being dragged
16156          * @method handleMouseDown
16157          * @param {Event} e the event
16158          * @param oDD the DragDrop object being dragged
16159          * @private
16160          * @static
16161          */
16162         handleMouseDown: function(e, oDD) {
16163             if(Roo.QuickTips){
16164                 Roo.QuickTips.disable();
16165             }
16166             this.currentTarget = e.getTarget();
16167
16168             this.dragCurrent = oDD;
16169
16170             var el = oDD.getEl();
16171
16172             // track start position
16173             this.startX = e.getPageX();
16174             this.startY = e.getPageY();
16175
16176             this.deltaX = this.startX - el.offsetLeft;
16177             this.deltaY = this.startY - el.offsetTop;
16178
16179             this.dragThreshMet = false;
16180
16181             this.clickTimeout = setTimeout(
16182                     function() {
16183                         var DDM = Roo.dd.DDM;
16184                         DDM.startDrag(DDM.startX, DDM.startY);
16185                     },
16186                     this.clickTimeThresh );
16187         },
16188
16189         /**
16190          * Fired when either the drag pixel threshol or the mousedown hold
16191          * time threshold has been met.
16192          * @method startDrag
16193          * @param x {int} the X position of the original mousedown
16194          * @param y {int} the Y position of the original mousedown
16195          * @static
16196          */
16197         startDrag: function(x, y) {
16198             clearTimeout(this.clickTimeout);
16199             if (this.dragCurrent) {
16200                 this.dragCurrent.b4StartDrag(x, y);
16201                 this.dragCurrent.startDrag(x, y);
16202             }
16203             this.dragThreshMet = true;
16204         },
16205
16206         /**
16207          * Internal function to handle the mouseup event.  Will be invoked
16208          * from the context of the document.
16209          * @method handleMouseUp
16210          * @param {Event} e the event
16211          * @private
16212          * @static
16213          */
16214         handleMouseUp: function(e) {
16215
16216             if(Roo.QuickTips){
16217                 Roo.QuickTips.enable();
16218             }
16219             if (! this.dragCurrent) {
16220                 return;
16221             }
16222
16223             clearTimeout(this.clickTimeout);
16224
16225             if (this.dragThreshMet) {
16226                 this.fireEvents(e, true);
16227             } else {
16228             }
16229
16230             this.stopDrag(e);
16231
16232             this.stopEvent(e);
16233         },
16234
16235         /**
16236          * Utility to stop event propagation and event default, if these
16237          * features are turned on.
16238          * @method stopEvent
16239          * @param {Event} e the event as returned by this.getEvent()
16240          * @static
16241          */
16242         stopEvent: function(e){
16243             if(this.stopPropagation) {
16244                 e.stopPropagation();
16245             }
16246
16247             if (this.preventDefault) {
16248                 e.preventDefault();
16249             }
16250         },
16251
16252         /**
16253          * Internal function to clean up event handlers after the drag
16254          * operation is complete
16255          * @method stopDrag
16256          * @param {Event} e the event
16257          * @private
16258          * @static
16259          */
16260         stopDrag: function(e) {
16261             // Fire the drag end event for the item that was dragged
16262             if (this.dragCurrent) {
16263                 if (this.dragThreshMet) {
16264                     this.dragCurrent.b4EndDrag(e);
16265                     this.dragCurrent.endDrag(e);
16266                 }
16267
16268                 this.dragCurrent.onMouseUp(e);
16269             }
16270
16271             this.dragCurrent = null;
16272             this.dragOvers = {};
16273         },
16274
16275         /**
16276          * Internal function to handle the mousemove event.  Will be invoked
16277          * from the context of the html element.
16278          *
16279          * @TODO figure out what we can do about mouse events lost when the
16280          * user drags objects beyond the window boundary.  Currently we can
16281          * detect this in internet explorer by verifying that the mouse is
16282          * down during the mousemove event.  Firefox doesn't give us the
16283          * button state on the mousemove event.
16284          * @method handleMouseMove
16285          * @param {Event} e the event
16286          * @private
16287          * @static
16288          */
16289         handleMouseMove: function(e) {
16290             if (! this.dragCurrent) {
16291                 return true;
16292             }
16293
16294             // var button = e.which || e.button;
16295
16296             // check for IE mouseup outside of page boundary
16297             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16298                 this.stopEvent(e);
16299                 return this.handleMouseUp(e);
16300             }
16301
16302             if (!this.dragThreshMet) {
16303                 var diffX = Math.abs(this.startX - e.getPageX());
16304                 var diffY = Math.abs(this.startY - e.getPageY());
16305                 if (diffX > this.clickPixelThresh ||
16306                             diffY > this.clickPixelThresh) {
16307                     this.startDrag(this.startX, this.startY);
16308                 }
16309             }
16310
16311             if (this.dragThreshMet) {
16312                 this.dragCurrent.b4Drag(e);
16313                 this.dragCurrent.onDrag(e);
16314                 if(!this.dragCurrent.moveOnly){
16315                     this.fireEvents(e, false);
16316                 }
16317             }
16318
16319             this.stopEvent(e);
16320
16321             return true;
16322         },
16323
16324         /**
16325          * Iterates over all of the DragDrop elements to find ones we are
16326          * hovering over or dropping on
16327          * @method fireEvents
16328          * @param {Event} e the event
16329          * @param {boolean} isDrop is this a drop op or a mouseover op?
16330          * @private
16331          * @static
16332          */
16333         fireEvents: function(e, isDrop) {
16334             var dc = this.dragCurrent;
16335
16336             // If the user did the mouse up outside of the window, we could
16337             // get here even though we have ended the drag.
16338             if (!dc || dc.isLocked()) {
16339                 return;
16340             }
16341
16342             var pt = e.getPoint();
16343
16344             // cache the previous dragOver array
16345             var oldOvers = [];
16346
16347             var outEvts   = [];
16348             var overEvts  = [];
16349             var dropEvts  = [];
16350             var enterEvts = [];
16351
16352             // Check to see if the object(s) we were hovering over is no longer
16353             // being hovered over so we can fire the onDragOut event
16354             for (var i in this.dragOvers) {
16355
16356                 var ddo = this.dragOvers[i];
16357
16358                 if (! this.isTypeOfDD(ddo)) {
16359                     continue;
16360                 }
16361
16362                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16363                     outEvts.push( ddo );
16364                 }
16365
16366                 oldOvers[i] = true;
16367                 delete this.dragOvers[i];
16368             }
16369
16370             for (var sGroup in dc.groups) {
16371
16372                 if ("string" != typeof sGroup) {
16373                     continue;
16374                 }
16375
16376                 for (i in this.ids[sGroup]) {
16377                     var oDD = this.ids[sGroup][i];
16378                     if (! this.isTypeOfDD(oDD)) {
16379                         continue;
16380                     }
16381
16382                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16383                         if (this.isOverTarget(pt, oDD, this.mode)) {
16384                             // look for drop interactions
16385                             if (isDrop) {
16386                                 dropEvts.push( oDD );
16387                             // look for drag enter and drag over interactions
16388                             } else {
16389
16390                                 // initial drag over: dragEnter fires
16391                                 if (!oldOvers[oDD.id]) {
16392                                     enterEvts.push( oDD );
16393                                 // subsequent drag overs: dragOver fires
16394                                 } else {
16395                                     overEvts.push( oDD );
16396                                 }
16397
16398                                 this.dragOvers[oDD.id] = oDD;
16399                             }
16400                         }
16401                     }
16402                 }
16403             }
16404
16405             if (this.mode) {
16406                 if (outEvts.length) {
16407                     dc.b4DragOut(e, outEvts);
16408                     dc.onDragOut(e, outEvts);
16409                 }
16410
16411                 if (enterEvts.length) {
16412                     dc.onDragEnter(e, enterEvts);
16413                 }
16414
16415                 if (overEvts.length) {
16416                     dc.b4DragOver(e, overEvts);
16417                     dc.onDragOver(e, overEvts);
16418                 }
16419
16420                 if (dropEvts.length) {
16421                     dc.b4DragDrop(e, dropEvts);
16422                     dc.onDragDrop(e, dropEvts);
16423                 }
16424
16425             } else {
16426                 // fire dragout events
16427                 var len = 0;
16428                 for (i=0, len=outEvts.length; i<len; ++i) {
16429                     dc.b4DragOut(e, outEvts[i].id);
16430                     dc.onDragOut(e, outEvts[i].id);
16431                 }
16432
16433                 // fire enter events
16434                 for (i=0,len=enterEvts.length; i<len; ++i) {
16435                     // dc.b4DragEnter(e, oDD.id);
16436                     dc.onDragEnter(e, enterEvts[i].id);
16437                 }
16438
16439                 // fire over events
16440                 for (i=0,len=overEvts.length; i<len; ++i) {
16441                     dc.b4DragOver(e, overEvts[i].id);
16442                     dc.onDragOver(e, overEvts[i].id);
16443                 }
16444
16445                 // fire drop events
16446                 for (i=0, len=dropEvts.length; i<len; ++i) {
16447                     dc.b4DragDrop(e, dropEvts[i].id);
16448                     dc.onDragDrop(e, dropEvts[i].id);
16449                 }
16450
16451             }
16452
16453             // notify about a drop that did not find a target
16454             if (isDrop && !dropEvts.length) {
16455                 dc.onInvalidDrop(e);
16456             }
16457
16458         },
16459
16460         /**
16461          * Helper function for getting the best match from the list of drag
16462          * and drop objects returned by the drag and drop events when we are
16463          * in INTERSECT mode.  It returns either the first object that the
16464          * cursor is over, or the object that has the greatest overlap with
16465          * the dragged element.
16466          * @method getBestMatch
16467          * @param  {DragDrop[]} dds The array of drag and drop objects
16468          * targeted
16469          * @return {DragDrop}       The best single match
16470          * @static
16471          */
16472         getBestMatch: function(dds) {
16473             var winner = null;
16474             // Return null if the input is not what we expect
16475             //if (!dds || !dds.length || dds.length == 0) {
16476                // winner = null;
16477             // If there is only one item, it wins
16478             //} else if (dds.length == 1) {
16479
16480             var len = dds.length;
16481
16482             if (len == 1) {
16483                 winner = dds[0];
16484             } else {
16485                 // Loop through the targeted items
16486                 for (var i=0; i<len; ++i) {
16487                     var dd = dds[i];
16488                     // If the cursor is over the object, it wins.  If the
16489                     // cursor is over multiple matches, the first one we come
16490                     // to wins.
16491                     if (dd.cursorIsOver) {
16492                         winner = dd;
16493                         break;
16494                     // Otherwise the object with the most overlap wins
16495                     } else {
16496                         if (!winner ||
16497                             winner.overlap.getArea() < dd.overlap.getArea()) {
16498                             winner = dd;
16499                         }
16500                     }
16501                 }
16502             }
16503
16504             return winner;
16505         },
16506
16507         /**
16508          * Refreshes the cache of the top-left and bottom-right points of the
16509          * drag and drop objects in the specified group(s).  This is in the
16510          * format that is stored in the drag and drop instance, so typical
16511          * usage is:
16512          * <code>
16513          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16514          * </code>
16515          * Alternatively:
16516          * <code>
16517          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16518          * </code>
16519          * @TODO this really should be an indexed array.  Alternatively this
16520          * method could accept both.
16521          * @method refreshCache
16522          * @param {Object} groups an associative array of groups to refresh
16523          * @static
16524          */
16525         refreshCache: function(groups) {
16526             for (var sGroup in groups) {
16527                 if ("string" != typeof sGroup) {
16528                     continue;
16529                 }
16530                 for (var i in this.ids[sGroup]) {
16531                     var oDD = this.ids[sGroup][i];
16532
16533                     if (this.isTypeOfDD(oDD)) {
16534                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16535                         var loc = this.getLocation(oDD);
16536                         if (loc) {
16537                             this.locationCache[oDD.id] = loc;
16538                         } else {
16539                             delete this.locationCache[oDD.id];
16540                             // this will unregister the drag and drop object if
16541                             // the element is not in a usable state
16542                             // oDD.unreg();
16543                         }
16544                     }
16545                 }
16546             }
16547         },
16548
16549         /**
16550          * This checks to make sure an element exists and is in the DOM.  The
16551          * main purpose is to handle cases where innerHTML is used to remove
16552          * drag and drop objects from the DOM.  IE provides an 'unspecified
16553          * error' when trying to access the offsetParent of such an element
16554          * @method verifyEl
16555          * @param {HTMLElement} el the element to check
16556          * @return {boolean} true if the element looks usable
16557          * @static
16558          */
16559         verifyEl: function(el) {
16560             if (el) {
16561                 var parent;
16562                 if(Roo.isIE){
16563                     try{
16564                         parent = el.offsetParent;
16565                     }catch(e){}
16566                 }else{
16567                     parent = el.offsetParent;
16568                 }
16569                 if (parent) {
16570                     return true;
16571                 }
16572             }
16573
16574             return false;
16575         },
16576
16577         /**
16578          * Returns a Region object containing the drag and drop element's position
16579          * and size, including the padding configured for it
16580          * @method getLocation
16581          * @param {DragDrop} oDD the drag and drop object to get the
16582          *                       location for
16583          * @return {Roo.lib.Region} a Region object representing the total area
16584          *                             the element occupies, including any padding
16585          *                             the instance is configured for.
16586          * @static
16587          */
16588         getLocation: function(oDD) {
16589             if (! this.isTypeOfDD(oDD)) {
16590                 return null;
16591             }
16592
16593             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16594
16595             try {
16596                 pos= Roo.lib.Dom.getXY(el);
16597             } catch (e) { }
16598
16599             if (!pos) {
16600                 return null;
16601             }
16602
16603             x1 = pos[0];
16604             x2 = x1 + el.offsetWidth;
16605             y1 = pos[1];
16606             y2 = y1 + el.offsetHeight;
16607
16608             t = y1 - oDD.padding[0];
16609             r = x2 + oDD.padding[1];
16610             b = y2 + oDD.padding[2];
16611             l = x1 - oDD.padding[3];
16612
16613             return new Roo.lib.Region( t, r, b, l );
16614         },
16615
16616         /**
16617          * Checks the cursor location to see if it over the target
16618          * @method isOverTarget
16619          * @param {Roo.lib.Point} pt The point to evaluate
16620          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16621          * @return {boolean} true if the mouse is over the target
16622          * @private
16623          * @static
16624          */
16625         isOverTarget: function(pt, oTarget, intersect) {
16626             // use cache if available
16627             var loc = this.locationCache[oTarget.id];
16628             if (!loc || !this.useCache) {
16629                 loc = this.getLocation(oTarget);
16630                 this.locationCache[oTarget.id] = loc;
16631
16632             }
16633
16634             if (!loc) {
16635                 return false;
16636             }
16637
16638             oTarget.cursorIsOver = loc.contains( pt );
16639
16640             // DragDrop is using this as a sanity check for the initial mousedown
16641             // in this case we are done.  In POINT mode, if the drag obj has no
16642             // contraints, we are also done. Otherwise we need to evaluate the
16643             // location of the target as related to the actual location of the
16644             // dragged element.
16645             var dc = this.dragCurrent;
16646             if (!dc || !dc.getTargetCoord ||
16647                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16648                 return oTarget.cursorIsOver;
16649             }
16650
16651             oTarget.overlap = null;
16652
16653             // Get the current location of the drag element, this is the
16654             // location of the mouse event less the delta that represents
16655             // where the original mousedown happened on the element.  We
16656             // need to consider constraints and ticks as well.
16657             var pos = dc.getTargetCoord(pt.x, pt.y);
16658
16659             var el = dc.getDragEl();
16660             var curRegion = new Roo.lib.Region( pos.y,
16661                                                    pos.x + el.offsetWidth,
16662                                                    pos.y + el.offsetHeight,
16663                                                    pos.x );
16664
16665             var overlap = curRegion.intersect(loc);
16666
16667             if (overlap) {
16668                 oTarget.overlap = overlap;
16669                 return (intersect) ? true : oTarget.cursorIsOver;
16670             } else {
16671                 return false;
16672             }
16673         },
16674
16675         /**
16676          * unload event handler
16677          * @method _onUnload
16678          * @private
16679          * @static
16680          */
16681         _onUnload: function(e, me) {
16682             Roo.dd.DragDropMgr.unregAll();
16683         },
16684
16685         /**
16686          * Cleans up the drag and drop events and objects.
16687          * @method unregAll
16688          * @private
16689          * @static
16690          */
16691         unregAll: function() {
16692
16693             if (this.dragCurrent) {
16694                 this.stopDrag();
16695                 this.dragCurrent = null;
16696             }
16697
16698             this._execOnAll("unreg", []);
16699
16700             for (i in this.elementCache) {
16701                 delete this.elementCache[i];
16702             }
16703
16704             this.elementCache = {};
16705             this.ids = {};
16706         },
16707
16708         /**
16709          * A cache of DOM elements
16710          * @property elementCache
16711          * @private
16712          * @static
16713          */
16714         elementCache: {},
16715
16716         /**
16717          * Get the wrapper for the DOM element specified
16718          * @method getElWrapper
16719          * @param {String} id the id of the element to get
16720          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16721          * @private
16722          * @deprecated This wrapper isn't that useful
16723          * @static
16724          */
16725         getElWrapper: function(id) {
16726             var oWrapper = this.elementCache[id];
16727             if (!oWrapper || !oWrapper.el) {
16728                 oWrapper = this.elementCache[id] =
16729                     new this.ElementWrapper(Roo.getDom(id));
16730             }
16731             return oWrapper;
16732         },
16733
16734         /**
16735          * Returns the actual DOM element
16736          * @method getElement
16737          * @param {String} id the id of the elment to get
16738          * @return {Object} The element
16739          * @deprecated use Roo.getDom instead
16740          * @static
16741          */
16742         getElement: function(id) {
16743             return Roo.getDom(id);
16744         },
16745
16746         /**
16747          * Returns the style property for the DOM element (i.e.,
16748          * document.getElById(id).style)
16749          * @method getCss
16750          * @param {String} id the id of the elment to get
16751          * @return {Object} The style property of the element
16752          * @deprecated use Roo.getDom instead
16753          * @static
16754          */
16755         getCss: function(id) {
16756             var el = Roo.getDom(id);
16757             return (el) ? el.style : null;
16758         },
16759
16760         /**
16761          * Inner class for cached elements
16762          * @class DragDropMgr.ElementWrapper
16763          * @for DragDropMgr
16764          * @private
16765          * @deprecated
16766          */
16767         ElementWrapper: function(el) {
16768                 /**
16769                  * The element
16770                  * @property el
16771                  */
16772                 this.el = el || null;
16773                 /**
16774                  * The element id
16775                  * @property id
16776                  */
16777                 this.id = this.el && el.id;
16778                 /**
16779                  * A reference to the style property
16780                  * @property css
16781                  */
16782                 this.css = this.el && el.style;
16783             },
16784
16785         /**
16786          * Returns the X position of an html element
16787          * @method getPosX
16788          * @param el the element for which to get the position
16789          * @return {int} the X coordinate
16790          * @for DragDropMgr
16791          * @deprecated use Roo.lib.Dom.getX instead
16792          * @static
16793          */
16794         getPosX: function(el) {
16795             return Roo.lib.Dom.getX(el);
16796         },
16797
16798         /**
16799          * Returns the Y position of an html element
16800          * @method getPosY
16801          * @param el the element for which to get the position
16802          * @return {int} the Y coordinate
16803          * @deprecated use Roo.lib.Dom.getY instead
16804          * @static
16805          */
16806         getPosY: function(el) {
16807             return Roo.lib.Dom.getY(el);
16808         },
16809
16810         /**
16811          * Swap two nodes.  In IE, we use the native method, for others we
16812          * emulate the IE behavior
16813          * @method swapNode
16814          * @param n1 the first node to swap
16815          * @param n2 the other node to swap
16816          * @static
16817          */
16818         swapNode: function(n1, n2) {
16819             if (n1.swapNode) {
16820                 n1.swapNode(n2);
16821             } else {
16822                 var p = n2.parentNode;
16823                 var s = n2.nextSibling;
16824
16825                 if (s == n1) {
16826                     p.insertBefore(n1, n2);
16827                 } else if (n2 == n1.nextSibling) {
16828                     p.insertBefore(n2, n1);
16829                 } else {
16830                     n1.parentNode.replaceChild(n2, n1);
16831                     p.insertBefore(n1, s);
16832                 }
16833             }
16834         },
16835
16836         /**
16837          * Returns the current scroll position
16838          * @method getScroll
16839          * @private
16840          * @static
16841          */
16842         getScroll: function () {
16843             var t, l, dde=document.documentElement, db=document.body;
16844             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16845                 t = dde.scrollTop;
16846                 l = dde.scrollLeft;
16847             } else if (db) {
16848                 t = db.scrollTop;
16849                 l = db.scrollLeft;
16850             } else {
16851
16852             }
16853             return { top: t, left: l };
16854         },
16855
16856         /**
16857          * Returns the specified element style property
16858          * @method getStyle
16859          * @param {HTMLElement} el          the element
16860          * @param {string}      styleProp   the style property
16861          * @return {string} The value of the style property
16862          * @deprecated use Roo.lib.Dom.getStyle
16863          * @static
16864          */
16865         getStyle: function(el, styleProp) {
16866             return Roo.fly(el).getStyle(styleProp);
16867         },
16868
16869         /**
16870          * Gets the scrollTop
16871          * @method getScrollTop
16872          * @return {int} the document's scrollTop
16873          * @static
16874          */
16875         getScrollTop: function () { return this.getScroll().top; },
16876
16877         /**
16878          * Gets the scrollLeft
16879          * @method getScrollLeft
16880          * @return {int} the document's scrollTop
16881          * @static
16882          */
16883         getScrollLeft: function () { return this.getScroll().left; },
16884
16885         /**
16886          * Sets the x/y position of an element to the location of the
16887          * target element.
16888          * @method moveToEl
16889          * @param {HTMLElement} moveEl      The element to move
16890          * @param {HTMLElement} targetEl    The position reference element
16891          * @static
16892          */
16893         moveToEl: function (moveEl, targetEl) {
16894             var aCoord = Roo.lib.Dom.getXY(targetEl);
16895             Roo.lib.Dom.setXY(moveEl, aCoord);
16896         },
16897
16898         /**
16899          * Numeric array sort function
16900          * @method numericSort
16901          * @static
16902          */
16903         numericSort: function(a, b) { return (a - b); },
16904
16905         /**
16906          * Internal counter
16907          * @property _timeoutCount
16908          * @private
16909          * @static
16910          */
16911         _timeoutCount: 0,
16912
16913         /**
16914          * Trying to make the load order less important.  Without this we get
16915          * an error if this file is loaded before the Event Utility.
16916          * @method _addListeners
16917          * @private
16918          * @static
16919          */
16920         _addListeners: function() {
16921             var DDM = Roo.dd.DDM;
16922             if ( Roo.lib.Event && document ) {
16923                 DDM._onLoad();
16924             } else {
16925                 if (DDM._timeoutCount > 2000) {
16926                 } else {
16927                     setTimeout(DDM._addListeners, 10);
16928                     if (document && document.body) {
16929                         DDM._timeoutCount += 1;
16930                     }
16931                 }
16932             }
16933         },
16934
16935         /**
16936          * Recursively searches the immediate parent and all child nodes for
16937          * the handle element in order to determine wheter or not it was
16938          * clicked.
16939          * @method handleWasClicked
16940          * @param node the html element to inspect
16941          * @static
16942          */
16943         handleWasClicked: function(node, id) {
16944             if (this.isHandle(id, node.id)) {
16945                 return true;
16946             } else {
16947                 // check to see if this is a text node child of the one we want
16948                 var p = node.parentNode;
16949
16950                 while (p) {
16951                     if (this.isHandle(id, p.id)) {
16952                         return true;
16953                     } else {
16954                         p = p.parentNode;
16955                     }
16956                 }
16957             }
16958
16959             return false;
16960         }
16961
16962     };
16963
16964 }();
16965
16966 // shorter alias, save a few bytes
16967 Roo.dd.DDM = Roo.dd.DragDropMgr;
16968 Roo.dd.DDM._addListeners();
16969
16970 }/*
16971  * Based on:
16972  * Ext JS Library 1.1.1
16973  * Copyright(c) 2006-2007, Ext JS, LLC.
16974  *
16975  * Originally Released Under LGPL - original licence link has changed is not relivant.
16976  *
16977  * Fork - LGPL
16978  * <script type="text/javascript">
16979  */
16980
16981 /**
16982  * @class Roo.dd.DD
16983  * A DragDrop implementation where the linked element follows the
16984  * mouse cursor during a drag.
16985  * @extends Roo.dd.DragDrop
16986  * @constructor
16987  * @param {String} id the id of the linked element
16988  * @param {String} sGroup the group of related DragDrop items
16989  * @param {object} config an object containing configurable attributes
16990  *                Valid properties for DD:
16991  *                    scroll
16992  */
16993 Roo.dd.DD = function(id, sGroup, config) {
16994     if (id) {
16995         this.init(id, sGroup, config);
16996     }
16997 };
16998
16999 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17000
17001     /**
17002      * When set to true, the utility automatically tries to scroll the browser
17003      * window wehn a drag and drop element is dragged near the viewport boundary.
17004      * Defaults to true.
17005      * @property scroll
17006      * @type boolean
17007      */
17008     scroll: true,
17009
17010     /**
17011      * Sets the pointer offset to the distance between the linked element's top
17012      * left corner and the location the element was clicked
17013      * @method autoOffset
17014      * @param {int} iPageX the X coordinate of the click
17015      * @param {int} iPageY the Y coordinate of the click
17016      */
17017     autoOffset: function(iPageX, iPageY) {
17018         var x = iPageX - this.startPageX;
17019         var y = iPageY - this.startPageY;
17020         this.setDelta(x, y);
17021     },
17022
17023     /**
17024      * Sets the pointer offset.  You can call this directly to force the
17025      * offset to be in a particular location (e.g., pass in 0,0 to set it
17026      * to the center of the object)
17027      * @method setDelta
17028      * @param {int} iDeltaX the distance from the left
17029      * @param {int} iDeltaY the distance from the top
17030      */
17031     setDelta: function(iDeltaX, iDeltaY) {
17032         this.deltaX = iDeltaX;
17033         this.deltaY = iDeltaY;
17034     },
17035
17036     /**
17037      * Sets the drag element to the location of the mousedown or click event,
17038      * maintaining the cursor location relative to the location on the element
17039      * that was clicked.  Override this if you want to place the element in a
17040      * location other than where the cursor is.
17041      * @method setDragElPos
17042      * @param {int} iPageX the X coordinate of the mousedown or drag event
17043      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17044      */
17045     setDragElPos: function(iPageX, iPageY) {
17046         // the first time we do this, we are going to check to make sure
17047         // the element has css positioning
17048
17049         var el = this.getDragEl();
17050         this.alignElWithMouse(el, iPageX, iPageY);
17051     },
17052
17053     /**
17054      * Sets the element to the location of the mousedown or click event,
17055      * maintaining the cursor location relative to the location on the element
17056      * that was clicked.  Override this if you want to place the element in a
17057      * location other than where the cursor is.
17058      * @method alignElWithMouse
17059      * @param {HTMLElement} el the element to move
17060      * @param {int} iPageX the X coordinate of the mousedown or drag event
17061      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17062      */
17063     alignElWithMouse: function(el, iPageX, iPageY) {
17064         var oCoord = this.getTargetCoord(iPageX, iPageY);
17065         var fly = el.dom ? el : Roo.fly(el);
17066         if (!this.deltaSetXY) {
17067             var aCoord = [oCoord.x, oCoord.y];
17068             fly.setXY(aCoord);
17069             var newLeft = fly.getLeft(true);
17070             var newTop  = fly.getTop(true);
17071             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17072         } else {
17073             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17074         }
17075
17076         this.cachePosition(oCoord.x, oCoord.y);
17077         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17078         return oCoord;
17079     },
17080
17081     /**
17082      * Saves the most recent position so that we can reset the constraints and
17083      * tick marks on-demand.  We need to know this so that we can calculate the
17084      * number of pixels the element is offset from its original position.
17085      * @method cachePosition
17086      * @param iPageX the current x position (optional, this just makes it so we
17087      * don't have to look it up again)
17088      * @param iPageY the current y position (optional, this just makes it so we
17089      * don't have to look it up again)
17090      */
17091     cachePosition: function(iPageX, iPageY) {
17092         if (iPageX) {
17093             this.lastPageX = iPageX;
17094             this.lastPageY = iPageY;
17095         } else {
17096             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17097             this.lastPageX = aCoord[0];
17098             this.lastPageY = aCoord[1];
17099         }
17100     },
17101
17102     /**
17103      * Auto-scroll the window if the dragged object has been moved beyond the
17104      * visible window boundary.
17105      * @method autoScroll
17106      * @param {int} x the drag element's x position
17107      * @param {int} y the drag element's y position
17108      * @param {int} h the height of the drag element
17109      * @param {int} w the width of the drag element
17110      * @private
17111      */
17112     autoScroll: function(x, y, h, w) {
17113
17114         if (this.scroll) {
17115             // The client height
17116             var clientH = Roo.lib.Dom.getViewWidth();
17117
17118             // The client width
17119             var clientW = Roo.lib.Dom.getViewHeight();
17120
17121             // The amt scrolled down
17122             var st = this.DDM.getScrollTop();
17123
17124             // The amt scrolled right
17125             var sl = this.DDM.getScrollLeft();
17126
17127             // Location of the bottom of the element
17128             var bot = h + y;
17129
17130             // Location of the right of the element
17131             var right = w + x;
17132
17133             // The distance from the cursor to the bottom of the visible area,
17134             // adjusted so that we don't scroll if the cursor is beyond the
17135             // element drag constraints
17136             var toBot = (clientH + st - y - this.deltaY);
17137
17138             // The distance from the cursor to the right of the visible area
17139             var toRight = (clientW + sl - x - this.deltaX);
17140
17141
17142             // How close to the edge the cursor must be before we scroll
17143             // var thresh = (document.all) ? 100 : 40;
17144             var thresh = 40;
17145
17146             // How many pixels to scroll per autoscroll op.  This helps to reduce
17147             // clunky scrolling. IE is more sensitive about this ... it needs this
17148             // value to be higher.
17149             var scrAmt = (document.all) ? 80 : 30;
17150
17151             // Scroll down if we are near the bottom of the visible page and the
17152             // obj extends below the crease
17153             if ( bot > clientH && toBot < thresh ) {
17154                 window.scrollTo(sl, st + scrAmt);
17155             }
17156
17157             // Scroll up if the window is scrolled down and the top of the object
17158             // goes above the top border
17159             if ( y < st && st > 0 && y - st < thresh ) {
17160                 window.scrollTo(sl, st - scrAmt);
17161             }
17162
17163             // Scroll right if the obj is beyond the right border and the cursor is
17164             // near the border.
17165             if ( right > clientW && toRight < thresh ) {
17166                 window.scrollTo(sl + scrAmt, st);
17167             }
17168
17169             // Scroll left if the window has been scrolled to the right and the obj
17170             // extends past the left border
17171             if ( x < sl && sl > 0 && x - sl < thresh ) {
17172                 window.scrollTo(sl - scrAmt, st);
17173             }
17174         }
17175     },
17176
17177     /**
17178      * Finds the location the element should be placed if we want to move
17179      * it to where the mouse location less the click offset would place us.
17180      * @method getTargetCoord
17181      * @param {int} iPageX the X coordinate of the click
17182      * @param {int} iPageY the Y coordinate of the click
17183      * @return an object that contains the coordinates (Object.x and Object.y)
17184      * @private
17185      */
17186     getTargetCoord: function(iPageX, iPageY) {
17187
17188
17189         var x = iPageX - this.deltaX;
17190         var y = iPageY - this.deltaY;
17191
17192         if (this.constrainX) {
17193             if (x < this.minX) { x = this.minX; }
17194             if (x > this.maxX) { x = this.maxX; }
17195         }
17196
17197         if (this.constrainY) {
17198             if (y < this.minY) { y = this.minY; }
17199             if (y > this.maxY) { y = this.maxY; }
17200         }
17201
17202         x = this.getTick(x, this.xTicks);
17203         y = this.getTick(y, this.yTicks);
17204
17205
17206         return {x:x, y:y};
17207     },
17208
17209     /*
17210      * Sets up config options specific to this class. Overrides
17211      * Roo.dd.DragDrop, but all versions of this method through the
17212      * inheritance chain are called
17213      */
17214     applyConfig: function() {
17215         Roo.dd.DD.superclass.applyConfig.call(this);
17216         this.scroll = (this.config.scroll !== false);
17217     },
17218
17219     /*
17220      * Event that fires prior to the onMouseDown event.  Overrides
17221      * Roo.dd.DragDrop.
17222      */
17223     b4MouseDown: function(e) {
17224         // this.resetConstraints();
17225         this.autoOffset(e.getPageX(),
17226                             e.getPageY());
17227     },
17228
17229     /*
17230      * Event that fires prior to the onDrag event.  Overrides
17231      * Roo.dd.DragDrop.
17232      */
17233     b4Drag: function(e) {
17234         this.setDragElPos(e.getPageX(),
17235                             e.getPageY());
17236     },
17237
17238     toString: function() {
17239         return ("DD " + this.id);
17240     }
17241
17242     //////////////////////////////////////////////////////////////////////////
17243     // Debugging ygDragDrop events that can be overridden
17244     //////////////////////////////////////////////////////////////////////////
17245     /*
17246     startDrag: function(x, y) {
17247     },
17248
17249     onDrag: function(e) {
17250     },
17251
17252     onDragEnter: function(e, id) {
17253     },
17254
17255     onDragOver: function(e, id) {
17256     },
17257
17258     onDragOut: function(e, id) {
17259     },
17260
17261     onDragDrop: function(e, id) {
17262     },
17263
17264     endDrag: function(e) {
17265     }
17266
17267     */
17268
17269 });/*
17270  * Based on:
17271  * Ext JS Library 1.1.1
17272  * Copyright(c) 2006-2007, Ext JS, LLC.
17273  *
17274  * Originally Released Under LGPL - original licence link has changed is not relivant.
17275  *
17276  * Fork - LGPL
17277  * <script type="text/javascript">
17278  */
17279
17280 /**
17281  * @class Roo.dd.DDProxy
17282  * A DragDrop implementation that inserts an empty, bordered div into
17283  * the document that follows the cursor during drag operations.  At the time of
17284  * the click, the frame div is resized to the dimensions of the linked html
17285  * element, and moved to the exact location of the linked element.
17286  *
17287  * References to the "frame" element refer to the single proxy element that
17288  * was created to be dragged in place of all DDProxy elements on the
17289  * page.
17290  *
17291  * @extends Roo.dd.DD
17292  * @constructor
17293  * @param {String} id the id of the linked html element
17294  * @param {String} sGroup the group of related DragDrop objects
17295  * @param {object} config an object containing configurable attributes
17296  *                Valid properties for DDProxy in addition to those in DragDrop:
17297  *                   resizeFrame, centerFrame, dragElId
17298  */
17299 Roo.dd.DDProxy = function(id, sGroup, config) {
17300     if (id) {
17301         this.init(id, sGroup, config);
17302         this.initFrame();
17303     }
17304 };
17305
17306 /**
17307  * The default drag frame div id
17308  * @property Roo.dd.DDProxy.dragElId
17309  * @type String
17310  * @static
17311  */
17312 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17313
17314 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17315
17316     /**
17317      * By default we resize the drag frame to be the same size as the element
17318      * we want to drag (this is to get the frame effect).  We can turn it off
17319      * if we want a different behavior.
17320      * @property resizeFrame
17321      * @type boolean
17322      */
17323     resizeFrame: true,
17324
17325     /**
17326      * By default the frame is positioned exactly where the drag element is, so
17327      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17328      * you do not have constraints on the obj is to have the drag frame centered
17329      * around the cursor.  Set centerFrame to true for this effect.
17330      * @property centerFrame
17331      * @type boolean
17332      */
17333     centerFrame: false,
17334
17335     /**
17336      * Creates the proxy element if it does not yet exist
17337      * @method createFrame
17338      */
17339     createFrame: function() {
17340         var self = this;
17341         var body = document.body;
17342
17343         if (!body || !body.firstChild) {
17344             setTimeout( function() { self.createFrame(); }, 50 );
17345             return;
17346         }
17347
17348         var div = this.getDragEl();
17349
17350         if (!div) {
17351             div    = document.createElement("div");
17352             div.id = this.dragElId;
17353             var s  = div.style;
17354
17355             s.position   = "absolute";
17356             s.visibility = "hidden";
17357             s.cursor     = "move";
17358             s.border     = "2px solid #aaa";
17359             s.zIndex     = 999;
17360
17361             // appendChild can blow up IE if invoked prior to the window load event
17362             // while rendering a table.  It is possible there are other scenarios
17363             // that would cause this to happen as well.
17364             body.insertBefore(div, body.firstChild);
17365         }
17366     },
17367
17368     /**
17369      * Initialization for the drag frame element.  Must be called in the
17370      * constructor of all subclasses
17371      * @method initFrame
17372      */
17373     initFrame: function() {
17374         this.createFrame();
17375     },
17376
17377     applyConfig: function() {
17378         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17379
17380         this.resizeFrame = (this.config.resizeFrame !== false);
17381         this.centerFrame = (this.config.centerFrame);
17382         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17383     },
17384
17385     /**
17386      * Resizes the drag frame to the dimensions of the clicked object, positions
17387      * it over the object, and finally displays it
17388      * @method showFrame
17389      * @param {int} iPageX X click position
17390      * @param {int} iPageY Y click position
17391      * @private
17392      */
17393     showFrame: function(iPageX, iPageY) {
17394         var el = this.getEl();
17395         var dragEl = this.getDragEl();
17396         var s = dragEl.style;
17397
17398         this._resizeProxy();
17399
17400         if (this.centerFrame) {
17401             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17402                            Math.round(parseInt(s.height, 10)/2) );
17403         }
17404
17405         this.setDragElPos(iPageX, iPageY);
17406
17407         Roo.fly(dragEl).show();
17408     },
17409
17410     /**
17411      * The proxy is automatically resized to the dimensions of the linked
17412      * element when a drag is initiated, unless resizeFrame is set to false
17413      * @method _resizeProxy
17414      * @private
17415      */
17416     _resizeProxy: function() {
17417         if (this.resizeFrame) {
17418             var el = this.getEl();
17419             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17420         }
17421     },
17422
17423     // overrides Roo.dd.DragDrop
17424     b4MouseDown: function(e) {
17425         var x = e.getPageX();
17426         var y = e.getPageY();
17427         this.autoOffset(x, y);
17428         this.setDragElPos(x, y);
17429     },
17430
17431     // overrides Roo.dd.DragDrop
17432     b4StartDrag: function(x, y) {
17433         // show the drag frame
17434         this.showFrame(x, y);
17435     },
17436
17437     // overrides Roo.dd.DragDrop
17438     b4EndDrag: function(e) {
17439         Roo.fly(this.getDragEl()).hide();
17440     },
17441
17442     // overrides Roo.dd.DragDrop
17443     // By default we try to move the element to the last location of the frame.
17444     // This is so that the default behavior mirrors that of Roo.dd.DD.
17445     endDrag: function(e) {
17446
17447         var lel = this.getEl();
17448         var del = this.getDragEl();
17449
17450         // Show the drag frame briefly so we can get its position
17451         del.style.visibility = "";
17452
17453         this.beforeMove();
17454         // Hide the linked element before the move to get around a Safari
17455         // rendering bug.
17456         lel.style.visibility = "hidden";
17457         Roo.dd.DDM.moveToEl(lel, del);
17458         del.style.visibility = "hidden";
17459         lel.style.visibility = "";
17460
17461         this.afterDrag();
17462     },
17463
17464     beforeMove : function(){
17465
17466     },
17467
17468     afterDrag : function(){
17469
17470     },
17471
17472     toString: function() {
17473         return ("DDProxy " + this.id);
17474     }
17475
17476 });
17477 /*
17478  * Based on:
17479  * Ext JS Library 1.1.1
17480  * Copyright(c) 2006-2007, Ext JS, LLC.
17481  *
17482  * Originally Released Under LGPL - original licence link has changed is not relivant.
17483  *
17484  * Fork - LGPL
17485  * <script type="text/javascript">
17486  */
17487
17488  /**
17489  * @class Roo.dd.DDTarget
17490  * A DragDrop implementation that does not move, but can be a drop
17491  * target.  You would get the same result by simply omitting implementation
17492  * for the event callbacks, but this way we reduce the processing cost of the
17493  * event listener and the callbacks.
17494  * @extends Roo.dd.DragDrop
17495  * @constructor
17496  * @param {String} id the id of the element that is a drop target
17497  * @param {String} sGroup the group of related DragDrop objects
17498  * @param {object} config an object containing configurable attributes
17499  *                 Valid properties for DDTarget in addition to those in
17500  *                 DragDrop:
17501  *                    none
17502  */
17503 Roo.dd.DDTarget = function(id, sGroup, config) {
17504     if (id) {
17505         this.initTarget(id, sGroup, config);
17506     }
17507     if (config.listeners || config.events) { 
17508        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17509             listeners : config.listeners || {}, 
17510             events : config.events || {} 
17511         });    
17512     }
17513 };
17514
17515 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17516 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17517     toString: function() {
17518         return ("DDTarget " + this.id);
17519     }
17520 });
17521 /*
17522  * Based on:
17523  * Ext JS Library 1.1.1
17524  * Copyright(c) 2006-2007, Ext JS, LLC.
17525  *
17526  * Originally Released Under LGPL - original licence link has changed is not relivant.
17527  *
17528  * Fork - LGPL
17529  * <script type="text/javascript">
17530  */
17531  
17532
17533 /**
17534  * @class Roo.dd.ScrollManager
17535  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17536  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17537  * @singleton
17538  */
17539 Roo.dd.ScrollManager = function(){
17540     var ddm = Roo.dd.DragDropMgr;
17541     var els = {};
17542     var dragEl = null;
17543     var proc = {};
17544     
17545     var onStop = function(e){
17546         dragEl = null;
17547         clearProc();
17548     };
17549     
17550     var triggerRefresh = function(){
17551         if(ddm.dragCurrent){
17552              ddm.refreshCache(ddm.dragCurrent.groups);
17553         }
17554     };
17555     
17556     var doScroll = function(){
17557         if(ddm.dragCurrent){
17558             var dds = Roo.dd.ScrollManager;
17559             if(!dds.animate){
17560                 if(proc.el.scroll(proc.dir, dds.increment)){
17561                     triggerRefresh();
17562                 }
17563             }else{
17564                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17565             }
17566         }
17567     };
17568     
17569     var clearProc = function(){
17570         if(proc.id){
17571             clearInterval(proc.id);
17572         }
17573         proc.id = 0;
17574         proc.el = null;
17575         proc.dir = "";
17576     };
17577     
17578     var startProc = function(el, dir){
17579         clearProc();
17580         proc.el = el;
17581         proc.dir = dir;
17582         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17583     };
17584     
17585     var onFire = function(e, isDrop){
17586         if(isDrop || !ddm.dragCurrent){ return; }
17587         var dds = Roo.dd.ScrollManager;
17588         if(!dragEl || dragEl != ddm.dragCurrent){
17589             dragEl = ddm.dragCurrent;
17590             // refresh regions on drag start
17591             dds.refreshCache();
17592         }
17593         
17594         var xy = Roo.lib.Event.getXY(e);
17595         var pt = new Roo.lib.Point(xy[0], xy[1]);
17596         for(var id in els){
17597             var el = els[id], r = el._region;
17598             if(r && r.contains(pt) && el.isScrollable()){
17599                 if(r.bottom - pt.y <= dds.thresh){
17600                     if(proc.el != el){
17601                         startProc(el, "down");
17602                     }
17603                     return;
17604                 }else if(r.right - pt.x <= dds.thresh){
17605                     if(proc.el != el){
17606                         startProc(el, "left");
17607                     }
17608                     return;
17609                 }else if(pt.y - r.top <= dds.thresh){
17610                     if(proc.el != el){
17611                         startProc(el, "up");
17612                     }
17613                     return;
17614                 }else if(pt.x - r.left <= dds.thresh){
17615                     if(proc.el != el){
17616                         startProc(el, "right");
17617                     }
17618                     return;
17619                 }
17620             }
17621         }
17622         clearProc();
17623     };
17624     
17625     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17626     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17627     
17628     return {
17629         /**
17630          * Registers new overflow element(s) to auto scroll
17631          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17632          */
17633         register : function(el){
17634             if(el instanceof Array){
17635                 for(var i = 0, len = el.length; i < len; i++) {
17636                         this.register(el[i]);
17637                 }
17638             }else{
17639                 el = Roo.get(el);
17640                 els[el.id] = el;
17641             }
17642         },
17643         
17644         /**
17645          * Unregisters overflow element(s) so they are no longer scrolled
17646          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17647          */
17648         unregister : function(el){
17649             if(el instanceof Array){
17650                 for(var i = 0, len = el.length; i < len; i++) {
17651                         this.unregister(el[i]);
17652                 }
17653             }else{
17654                 el = Roo.get(el);
17655                 delete els[el.id];
17656             }
17657         },
17658         
17659         /**
17660          * The number of pixels from the edge of a container the pointer needs to be to 
17661          * trigger scrolling (defaults to 25)
17662          * @type Number
17663          */
17664         thresh : 25,
17665         
17666         /**
17667          * The number of pixels to scroll in each scroll increment (defaults to 50)
17668          * @type Number
17669          */
17670         increment : 100,
17671         
17672         /**
17673          * The frequency of scrolls in milliseconds (defaults to 500)
17674          * @type Number
17675          */
17676         frequency : 500,
17677         
17678         /**
17679          * True to animate the scroll (defaults to true)
17680          * @type Boolean
17681          */
17682         animate: true,
17683         
17684         /**
17685          * The animation duration in seconds - 
17686          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17687          * @type Number
17688          */
17689         animDuration: .4,
17690         
17691         /**
17692          * Manually trigger a cache refresh.
17693          */
17694         refreshCache : function(){
17695             for(var id in els){
17696                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17697                     els[id]._region = els[id].getRegion();
17698                 }
17699             }
17700         }
17701     };
17702 }();/*
17703  * Based on:
17704  * Ext JS Library 1.1.1
17705  * Copyright(c) 2006-2007, Ext JS, LLC.
17706  *
17707  * Originally Released Under LGPL - original licence link has changed is not relivant.
17708  *
17709  * Fork - LGPL
17710  * <script type="text/javascript">
17711  */
17712  
17713
17714 /**
17715  * @class Roo.dd.Registry
17716  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17717  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17718  * @singleton
17719  */
17720 Roo.dd.Registry = function(){
17721     var elements = {}; 
17722     var handles = {}; 
17723     var autoIdSeed = 0;
17724
17725     var getId = function(el, autogen){
17726         if(typeof el == "string"){
17727             return el;
17728         }
17729         var id = el.id;
17730         if(!id && autogen !== false){
17731             id = "roodd-" + (++autoIdSeed);
17732             el.id = id;
17733         }
17734         return id;
17735     };
17736     
17737     return {
17738     /**
17739      * Register a drag drop element
17740      * @param {String|HTMLElement} element The id or DOM node to register
17741      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17742      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17743      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17744      * populated in the data object (if applicable):
17745      * <pre>
17746 Value      Description<br />
17747 ---------  ------------------------------------------<br />
17748 handles    Array of DOM nodes that trigger dragging<br />
17749            for the element being registered<br />
17750 isHandle   True if the element passed in triggers<br />
17751            dragging itself, else false
17752 </pre>
17753      */
17754         register : function(el, data){
17755             data = data || {};
17756             if(typeof el == "string"){
17757                 el = document.getElementById(el);
17758             }
17759             data.ddel = el;
17760             elements[getId(el)] = data;
17761             if(data.isHandle !== false){
17762                 handles[data.ddel.id] = data;
17763             }
17764             if(data.handles){
17765                 var hs = data.handles;
17766                 for(var i = 0, len = hs.length; i < len; i++){
17767                         handles[getId(hs[i])] = data;
17768                 }
17769             }
17770         },
17771
17772     /**
17773      * Unregister a drag drop element
17774      * @param {String|HTMLElement}  element The id or DOM node to unregister
17775      */
17776         unregister : function(el){
17777             var id = getId(el, false);
17778             var data = elements[id];
17779             if(data){
17780                 delete elements[id];
17781                 if(data.handles){
17782                     var hs = data.handles;
17783                     for(var i = 0, len = hs.length; i < len; i++){
17784                         delete handles[getId(hs[i], false)];
17785                     }
17786                 }
17787             }
17788         },
17789
17790     /**
17791      * Returns the handle registered for a DOM Node by id
17792      * @param {String|HTMLElement} id The DOM node or id to look up
17793      * @return {Object} handle The custom handle data
17794      */
17795         getHandle : function(id){
17796             if(typeof id != "string"){ // must be element?
17797                 id = id.id;
17798             }
17799             return handles[id];
17800         },
17801
17802     /**
17803      * Returns the handle that is registered for the DOM node that is the target of the event
17804      * @param {Event} e The event
17805      * @return {Object} handle The custom handle data
17806      */
17807         getHandleFromEvent : function(e){
17808             var t = Roo.lib.Event.getTarget(e);
17809             return t ? handles[t.id] : null;
17810         },
17811
17812     /**
17813      * Returns a custom data object that is registered for a DOM node by id
17814      * @param {String|HTMLElement} id The DOM node or id to look up
17815      * @return {Object} data The custom data
17816      */
17817         getTarget : function(id){
17818             if(typeof id != "string"){ // must be element?
17819                 id = id.id;
17820             }
17821             return elements[id];
17822         },
17823
17824     /**
17825      * Returns a custom data object that is registered for the DOM node that is the target of the event
17826      * @param {Event} e The event
17827      * @return {Object} data The custom data
17828      */
17829         getTargetFromEvent : function(e){
17830             var t = Roo.lib.Event.getTarget(e);
17831             return t ? elements[t.id] || handles[t.id] : null;
17832         }
17833     };
17834 }();/*
17835  * Based on:
17836  * Ext JS Library 1.1.1
17837  * Copyright(c) 2006-2007, Ext JS, LLC.
17838  *
17839  * Originally Released Under LGPL - original licence link has changed is not relivant.
17840  *
17841  * Fork - LGPL
17842  * <script type="text/javascript">
17843  */
17844  
17845
17846 /**
17847  * @class Roo.dd.StatusProxy
17848  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17849  * default drag proxy used by all Roo.dd components.
17850  * @constructor
17851  * @param {Object} config
17852  */
17853 Roo.dd.StatusProxy = function(config){
17854     Roo.apply(this, config);
17855     this.id = this.id || Roo.id();
17856     this.el = new Roo.Layer({
17857         dh: {
17858             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17859                 {tag: "div", cls: "x-dd-drop-icon"},
17860                 {tag: "div", cls: "x-dd-drag-ghost"}
17861             ]
17862         }, 
17863         shadow: !config || config.shadow !== false
17864     });
17865     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17866     this.dropStatus = this.dropNotAllowed;
17867 };
17868
17869 Roo.dd.StatusProxy.prototype = {
17870     /**
17871      * @cfg {String} dropAllowed
17872      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17873      */
17874     dropAllowed : "x-dd-drop-ok",
17875     /**
17876      * @cfg {String} dropNotAllowed
17877      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17878      */
17879     dropNotAllowed : "x-dd-drop-nodrop",
17880
17881     /**
17882      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17883      * over the current target element.
17884      * @param {String} cssClass The css class for the new drop status indicator image
17885      */
17886     setStatus : function(cssClass){
17887         cssClass = cssClass || this.dropNotAllowed;
17888         if(this.dropStatus != cssClass){
17889             this.el.replaceClass(this.dropStatus, cssClass);
17890             this.dropStatus = cssClass;
17891         }
17892     },
17893
17894     /**
17895      * Resets the status indicator to the default dropNotAllowed value
17896      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17897      */
17898     reset : function(clearGhost){
17899         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17900         this.dropStatus = this.dropNotAllowed;
17901         if(clearGhost){
17902             this.ghost.update("");
17903         }
17904     },
17905
17906     /**
17907      * Updates the contents of the ghost element
17908      * @param {String} html The html that will replace the current innerHTML of the ghost element
17909      */
17910     update : function(html){
17911         if(typeof html == "string"){
17912             this.ghost.update(html);
17913         }else{
17914             this.ghost.update("");
17915             html.style.margin = "0";
17916             this.ghost.dom.appendChild(html);
17917         }
17918         // ensure float = none set?? cant remember why though.
17919         var el = this.ghost.dom.firstChild;
17920                 if(el){
17921                         Roo.fly(el).setStyle('float', 'none');
17922                 }
17923     },
17924     
17925     /**
17926      * Returns the underlying proxy {@link Roo.Layer}
17927      * @return {Roo.Layer} el
17928     */
17929     getEl : function(){
17930         return this.el;
17931     },
17932
17933     /**
17934      * Returns the ghost element
17935      * @return {Roo.Element} el
17936      */
17937     getGhost : function(){
17938         return this.ghost;
17939     },
17940
17941     /**
17942      * Hides the proxy
17943      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17944      */
17945     hide : function(clear){
17946         this.el.hide();
17947         if(clear){
17948             this.reset(true);
17949         }
17950     },
17951
17952     /**
17953      * Stops the repair animation if it's currently running
17954      */
17955     stop : function(){
17956         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17957             this.anim.stop();
17958         }
17959     },
17960
17961     /**
17962      * Displays this proxy
17963      */
17964     show : function(){
17965         this.el.show();
17966     },
17967
17968     /**
17969      * Force the Layer to sync its shadow and shim positions to the element
17970      */
17971     sync : function(){
17972         this.el.sync();
17973     },
17974
17975     /**
17976      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17977      * invalid drop operation by the item being dragged.
17978      * @param {Array} xy The XY position of the element ([x, y])
17979      * @param {Function} callback The function to call after the repair is complete
17980      * @param {Object} scope The scope in which to execute the callback
17981      */
17982     repair : function(xy, callback, scope){
17983         this.callback = callback;
17984         this.scope = scope;
17985         if(xy && this.animRepair !== false){
17986             this.el.addClass("x-dd-drag-repair");
17987             this.el.hideUnders(true);
17988             this.anim = this.el.shift({
17989                 duration: this.repairDuration || .5,
17990                 easing: 'easeOut',
17991                 xy: xy,
17992                 stopFx: true,
17993                 callback: this.afterRepair,
17994                 scope: this
17995             });
17996         }else{
17997             this.afterRepair();
17998         }
17999     },
18000
18001     // private
18002     afterRepair : function(){
18003         this.hide(true);
18004         if(typeof this.callback == "function"){
18005             this.callback.call(this.scope || this);
18006         }
18007         this.callback = null;
18008         this.scope = null;
18009     }
18010 };/*
18011  * Based on:
18012  * Ext JS Library 1.1.1
18013  * Copyright(c) 2006-2007, Ext JS, LLC.
18014  *
18015  * Originally Released Under LGPL - original licence link has changed is not relivant.
18016  *
18017  * Fork - LGPL
18018  * <script type="text/javascript">
18019  */
18020
18021 /**
18022  * @class Roo.dd.DragSource
18023  * @extends Roo.dd.DDProxy
18024  * A simple class that provides the basic implementation needed to make any element draggable.
18025  * @constructor
18026  * @param {String/HTMLElement/Element} el The container element
18027  * @param {Object} config
18028  */
18029 Roo.dd.DragSource = function(el, config){
18030     this.el = Roo.get(el);
18031     this.dragData = {};
18032     
18033     Roo.apply(this, config);
18034     
18035     if(!this.proxy){
18036         this.proxy = new Roo.dd.StatusProxy();
18037     }
18038
18039     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18040           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18041     
18042     this.dragging = false;
18043 };
18044
18045 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18046     /**
18047      * @cfg {String} dropAllowed
18048      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18049      */
18050     dropAllowed : "x-dd-drop-ok",
18051     /**
18052      * @cfg {String} dropNotAllowed
18053      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18054      */
18055     dropNotAllowed : "x-dd-drop-nodrop",
18056
18057     /**
18058      * Returns the data object associated with this drag source
18059      * @return {Object} data An object containing arbitrary data
18060      */
18061     getDragData : function(e){
18062         return this.dragData;
18063     },
18064
18065     // private
18066     onDragEnter : function(e, id){
18067         var target = Roo.dd.DragDropMgr.getDDById(id);
18068         this.cachedTarget = target;
18069         if(this.beforeDragEnter(target, e, id) !== false){
18070             if(target.isNotifyTarget){
18071                 var status = target.notifyEnter(this, e, this.dragData);
18072                 this.proxy.setStatus(status);
18073             }else{
18074                 this.proxy.setStatus(this.dropAllowed);
18075             }
18076             
18077             if(this.afterDragEnter){
18078                 /**
18079                  * An empty function by default, but provided so that you can perform a custom action
18080                  * when the dragged item enters the drop target by providing an implementation.
18081                  * @param {Roo.dd.DragDrop} target The drop target
18082                  * @param {Event} e The event object
18083                  * @param {String} id The id of the dragged element
18084                  * @method afterDragEnter
18085                  */
18086                 this.afterDragEnter(target, e, id);
18087             }
18088         }
18089     },
18090
18091     /**
18092      * An empty function by default, but provided so that you can perform a custom action
18093      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18094      * @param {Roo.dd.DragDrop} target The drop target
18095      * @param {Event} e The event object
18096      * @param {String} id The id of the dragged element
18097      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18098      */
18099     beforeDragEnter : function(target, e, id){
18100         return true;
18101     },
18102
18103     // private
18104     alignElWithMouse: function() {
18105         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18106         this.proxy.sync();
18107     },
18108
18109     // private
18110     onDragOver : function(e, id){
18111         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18112         if(this.beforeDragOver(target, e, id) !== false){
18113             if(target.isNotifyTarget){
18114                 var status = target.notifyOver(this, e, this.dragData);
18115                 this.proxy.setStatus(status);
18116             }
18117
18118             if(this.afterDragOver){
18119                 /**
18120                  * An empty function by default, but provided so that you can perform a custom action
18121                  * while the dragged item is over the drop target by providing an implementation.
18122                  * @param {Roo.dd.DragDrop} target The drop target
18123                  * @param {Event} e The event object
18124                  * @param {String} id The id of the dragged element
18125                  * @method afterDragOver
18126                  */
18127                 this.afterDragOver(target, e, id);
18128             }
18129         }
18130     },
18131
18132     /**
18133      * An empty function by default, but provided so that you can perform a custom action
18134      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18135      * @param {Roo.dd.DragDrop} target The drop target
18136      * @param {Event} e The event object
18137      * @param {String} id The id of the dragged element
18138      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18139      */
18140     beforeDragOver : function(target, e, id){
18141         return true;
18142     },
18143
18144     // private
18145     onDragOut : function(e, id){
18146         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18147         if(this.beforeDragOut(target, e, id) !== false){
18148             if(target.isNotifyTarget){
18149                 target.notifyOut(this, e, this.dragData);
18150             }
18151             this.proxy.reset();
18152             if(this.afterDragOut){
18153                 /**
18154                  * An empty function by default, but provided so that you can perform a custom action
18155                  * after the dragged item is dragged out of the target without dropping.
18156                  * @param {Roo.dd.DragDrop} target The drop target
18157                  * @param {Event} e The event object
18158                  * @param {String} id The id of the dragged element
18159                  * @method afterDragOut
18160                  */
18161                 this.afterDragOut(target, e, id);
18162             }
18163         }
18164         this.cachedTarget = null;
18165     },
18166
18167     /**
18168      * An empty function by default, but provided so that you can perform a custom action before the dragged
18169      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18170      * @param {Roo.dd.DragDrop} target The drop target
18171      * @param {Event} e The event object
18172      * @param {String} id The id of the dragged element
18173      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18174      */
18175     beforeDragOut : function(target, e, id){
18176         return true;
18177     },
18178     
18179     // private
18180     onDragDrop : function(e, id){
18181         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18182         if(this.beforeDragDrop(target, e, id) !== false){
18183             if(target.isNotifyTarget){
18184                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18185                     this.onValidDrop(target, e, id);
18186                 }else{
18187                     this.onInvalidDrop(target, e, id);
18188                 }
18189             }else{
18190                 this.onValidDrop(target, e, id);
18191             }
18192             
18193             if(this.afterDragDrop){
18194                 /**
18195                  * An empty function by default, but provided so that you can perform a custom action
18196                  * after a valid drag drop has occurred by providing an implementation.
18197                  * @param {Roo.dd.DragDrop} target The drop target
18198                  * @param {Event} e The event object
18199                  * @param {String} id The id of the dropped element
18200                  * @method afterDragDrop
18201                  */
18202                 this.afterDragDrop(target, e, id);
18203             }
18204         }
18205         delete this.cachedTarget;
18206     },
18207
18208     /**
18209      * An empty function by default, but provided so that you can perform a custom action before the dragged
18210      * item is dropped onto the target and optionally cancel the onDragDrop.
18211      * @param {Roo.dd.DragDrop} target The drop target
18212      * @param {Event} e The event object
18213      * @param {String} id The id of the dragged element
18214      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18215      */
18216     beforeDragDrop : function(target, e, id){
18217         return true;
18218     },
18219
18220     // private
18221     onValidDrop : function(target, e, id){
18222         this.hideProxy();
18223         if(this.afterValidDrop){
18224             /**
18225              * An empty function by default, but provided so that you can perform a custom action
18226              * after a valid drop has occurred by providing an implementation.
18227              * @param {Object} target The target DD 
18228              * @param {Event} e The event object
18229              * @param {String} id The id of the dropped element
18230              * @method afterInvalidDrop
18231              */
18232             this.afterValidDrop(target, e, id);
18233         }
18234     },
18235
18236     // private
18237     getRepairXY : function(e, data){
18238         return this.el.getXY();  
18239     },
18240
18241     // private
18242     onInvalidDrop : function(target, e, id){
18243         this.beforeInvalidDrop(target, e, id);
18244         if(this.cachedTarget){
18245             if(this.cachedTarget.isNotifyTarget){
18246                 this.cachedTarget.notifyOut(this, e, this.dragData);
18247             }
18248             this.cacheTarget = null;
18249         }
18250         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18251
18252         if(this.afterInvalidDrop){
18253             /**
18254              * An empty function by default, but provided so that you can perform a custom action
18255              * after an invalid drop has occurred by providing an implementation.
18256              * @param {Event} e The event object
18257              * @param {String} id The id of the dropped element
18258              * @method afterInvalidDrop
18259              */
18260             this.afterInvalidDrop(e, id);
18261         }
18262     },
18263
18264     // private
18265     afterRepair : function(){
18266         if(Roo.enableFx){
18267             this.el.highlight(this.hlColor || "c3daf9");
18268         }
18269         this.dragging = false;
18270     },
18271
18272     /**
18273      * An empty function by default, but provided so that you can perform a custom action after an invalid
18274      * drop has occurred.
18275      * @param {Roo.dd.DragDrop} target The drop target
18276      * @param {Event} e The event object
18277      * @param {String} id The id of the dragged element
18278      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18279      */
18280     beforeInvalidDrop : function(target, e, id){
18281         return true;
18282     },
18283
18284     // private
18285     handleMouseDown : function(e){
18286         if(this.dragging) {
18287             return;
18288         }
18289         var data = this.getDragData(e);
18290         if(data && this.onBeforeDrag(data, e) !== false){
18291             this.dragData = data;
18292             this.proxy.stop();
18293             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18294         } 
18295     },
18296
18297     /**
18298      * An empty function by default, but provided so that you can perform a custom action before the initial
18299      * drag event begins and optionally cancel it.
18300      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18301      * @param {Event} e The event object
18302      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18303      */
18304     onBeforeDrag : function(data, e){
18305         return true;
18306     },
18307
18308     /**
18309      * An empty function by default, but provided so that you can perform a custom action once the initial
18310      * drag event has begun.  The drag cannot be canceled from this function.
18311      * @param {Number} x The x position of the click on the dragged object
18312      * @param {Number} y The y position of the click on the dragged object
18313      */
18314     onStartDrag : Roo.emptyFn,
18315
18316     // private - YUI override
18317     startDrag : function(x, y){
18318         this.proxy.reset();
18319         this.dragging = true;
18320         this.proxy.update("");
18321         this.onInitDrag(x, y);
18322         this.proxy.show();
18323     },
18324
18325     // private
18326     onInitDrag : function(x, y){
18327         var clone = this.el.dom.cloneNode(true);
18328         clone.id = Roo.id(); // prevent duplicate ids
18329         this.proxy.update(clone);
18330         this.onStartDrag(x, y);
18331         return true;
18332     },
18333
18334     /**
18335      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18336      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18337      */
18338     getProxy : function(){
18339         return this.proxy;  
18340     },
18341
18342     /**
18343      * Hides the drag source's {@link Roo.dd.StatusProxy}
18344      */
18345     hideProxy : function(){
18346         this.proxy.hide();  
18347         this.proxy.reset(true);
18348         this.dragging = false;
18349     },
18350
18351     // private
18352     triggerCacheRefresh : function(){
18353         Roo.dd.DDM.refreshCache(this.groups);
18354     },
18355
18356     // private - override to prevent hiding
18357     b4EndDrag: function(e) {
18358     },
18359
18360     // private - override to prevent moving
18361     endDrag : function(e){
18362         this.onEndDrag(this.dragData, e);
18363     },
18364
18365     // private
18366     onEndDrag : function(data, e){
18367     },
18368     
18369     // private - pin to cursor
18370     autoOffset : function(x, y) {
18371         this.setDelta(-12, -20);
18372     }    
18373 });/*
18374  * Based on:
18375  * Ext JS Library 1.1.1
18376  * Copyright(c) 2006-2007, Ext JS, LLC.
18377  *
18378  * Originally Released Under LGPL - original licence link has changed is not relivant.
18379  *
18380  * Fork - LGPL
18381  * <script type="text/javascript">
18382  */
18383
18384
18385 /**
18386  * @class Roo.dd.DropTarget
18387  * @extends Roo.dd.DDTarget
18388  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18389  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18390  * @constructor
18391  * @param {String/HTMLElement/Element} el The container element
18392  * @param {Object} config
18393  */
18394 Roo.dd.DropTarget = function(el, config){
18395     this.el = Roo.get(el);
18396     
18397     var listeners = false; ;
18398     if (config && config.listeners) {
18399         listeners= config.listeners;
18400         delete config.listeners;
18401     }
18402     Roo.apply(this, config);
18403     
18404     if(this.containerScroll){
18405         Roo.dd.ScrollManager.register(this.el);
18406     }
18407     this.addEvents( {
18408          /**
18409          * @scope Roo.dd.DropTarget
18410          */
18411          
18412          /**
18413          * @event enter
18414          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18415          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18416          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18417          * 
18418          * IMPORTANT : it should set this.overClass and this.dropAllowed
18419          * 
18420          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18421          * @param {Event} e The event
18422          * @param {Object} data An object containing arbitrary data supplied by the drag source
18423          */
18424         "enter" : true,
18425         
18426          /**
18427          * @event over
18428          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18429          * This method will be called on every mouse movement while the drag source is over the drop target.
18430          * This default implementation simply returns the dropAllowed config value.
18431          * 
18432          * IMPORTANT : it should set this.dropAllowed
18433          * 
18434          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18435          * @param {Event} e The event
18436          * @param {Object} data An object containing arbitrary data supplied by the drag source
18437          
18438          */
18439         "over" : true,
18440         /**
18441          * @event out
18442          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18443          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18444          * overClass (if any) from the drop element.
18445          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18446          * @param {Event} e The event
18447          * @param {Object} data An object containing arbitrary data supplied by the drag source
18448          */
18449          "out" : true,
18450          
18451         /**
18452          * @event drop
18453          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18454          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18455          * implementation that does something to process the drop event and returns true so that the drag source's
18456          * repair action does not run.
18457          * 
18458          * IMPORTANT : it should set this.success
18459          * 
18460          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18461          * @param {Event} e The event
18462          * @param {Object} data An object containing arbitrary data supplied by the drag source
18463         */
18464          "drop" : true
18465     });
18466             
18467      
18468     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18469         this.el.dom, 
18470         this.ddGroup || this.group,
18471         {
18472             isTarget: true,
18473             listeners : listeners || {} 
18474            
18475         
18476         }
18477     );
18478
18479 };
18480
18481 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18482     /**
18483      * @cfg {String} overClass
18484      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18485      */
18486      /**
18487      * @cfg {String} ddGroup
18488      * The drag drop group to handle drop events for
18489      */
18490      
18491     /**
18492      * @cfg {String} dropAllowed
18493      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18494      */
18495     dropAllowed : "x-dd-drop-ok",
18496     /**
18497      * @cfg {String} dropNotAllowed
18498      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18499      */
18500     dropNotAllowed : "x-dd-drop-nodrop",
18501     /**
18502      * @cfg {boolean} success
18503      * set this after drop listener.. 
18504      */
18505     success : false,
18506     /**
18507      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18508      * if the drop point is valid for over/enter..
18509      */
18510     valid : false,
18511     // private
18512     isTarget : true,
18513
18514     // private
18515     isNotifyTarget : true,
18516     
18517     /**
18518      * @hide
18519      */
18520     notifyEnter : function(dd, e, data)
18521     {
18522         this.valid = true;
18523         this.fireEvent('enter', dd, e, data);
18524         if(this.overClass){
18525             this.el.addClass(this.overClass);
18526         }
18527         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18528             this.valid ? this.dropAllowed : this.dropNotAllowed
18529         );
18530     },
18531
18532     /**
18533      * @hide
18534      */
18535     notifyOver : function(dd, e, data)
18536     {
18537         this.valid = true;
18538         this.fireEvent('over', dd, e, data);
18539         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18540             this.valid ? this.dropAllowed : this.dropNotAllowed
18541         );
18542     },
18543
18544     /**
18545      * @hide
18546      */
18547     notifyOut : function(dd, e, data)
18548     {
18549         this.fireEvent('out', dd, e, data);
18550         if(this.overClass){
18551             this.el.removeClass(this.overClass);
18552         }
18553     },
18554
18555     /**
18556      * @hide
18557      */
18558     notifyDrop : function(dd, e, data)
18559     {
18560         this.success = false;
18561         this.fireEvent('drop', dd, e, data);
18562         return this.success;
18563     }
18564 });/*
18565  * Based on:
18566  * Ext JS Library 1.1.1
18567  * Copyright(c) 2006-2007, Ext JS, LLC.
18568  *
18569  * Originally Released Under LGPL - original licence link has changed is not relivant.
18570  *
18571  * Fork - LGPL
18572  * <script type="text/javascript">
18573  */
18574
18575
18576 /**
18577  * @class Roo.dd.DragZone
18578  * @extends Roo.dd.DragSource
18579  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18580  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18581  * @constructor
18582  * @param {String/HTMLElement/Element} el The container element
18583  * @param {Object} config
18584  */
18585 Roo.dd.DragZone = function(el, config){
18586     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18587     if(this.containerScroll){
18588         Roo.dd.ScrollManager.register(this.el);
18589     }
18590 };
18591
18592 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18593     /**
18594      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18595      * for auto scrolling during drag operations.
18596      */
18597     /**
18598      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18599      * method after a failed drop (defaults to "c3daf9" - light blue)
18600      */
18601
18602     /**
18603      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18604      * for a valid target to drag based on the mouse down. Override this method
18605      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18606      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18607      * @param {EventObject} e The mouse down event
18608      * @return {Object} The dragData
18609      */
18610     getDragData : function(e){
18611         return Roo.dd.Registry.getHandleFromEvent(e);
18612     },
18613     
18614     /**
18615      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18616      * this.dragData.ddel
18617      * @param {Number} x The x position of the click on the dragged object
18618      * @param {Number} y The y position of the click on the dragged object
18619      * @return {Boolean} true to continue the drag, false to cancel
18620      */
18621     onInitDrag : function(x, y){
18622         this.proxy.update(this.dragData.ddel.cloneNode(true));
18623         this.onStartDrag(x, y);
18624         return true;
18625     },
18626     
18627     /**
18628      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18629      */
18630     afterRepair : function(){
18631         if(Roo.enableFx){
18632             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18633         }
18634         this.dragging = false;
18635     },
18636
18637     /**
18638      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18639      * the XY of this.dragData.ddel
18640      * @param {EventObject} e The mouse up event
18641      * @return {Array} The xy location (e.g. [100, 200])
18642      */
18643     getRepairXY : function(e){
18644         return Roo.Element.fly(this.dragData.ddel).getXY();  
18645     }
18646 });/*
18647  * Based on:
18648  * Ext JS Library 1.1.1
18649  * Copyright(c) 2006-2007, Ext JS, LLC.
18650  *
18651  * Originally Released Under LGPL - original licence link has changed is not relivant.
18652  *
18653  * Fork - LGPL
18654  * <script type="text/javascript">
18655  */
18656 /**
18657  * @class Roo.dd.DropZone
18658  * @extends Roo.dd.DropTarget
18659  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18660  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18661  * @constructor
18662  * @param {String/HTMLElement/Element} el The container element
18663  * @param {Object} config
18664  */
18665 Roo.dd.DropZone = function(el, config){
18666     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18667 };
18668
18669 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18670     /**
18671      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18672      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18673      * provide your own custom lookup.
18674      * @param {Event} e The event
18675      * @return {Object} data The custom data
18676      */
18677     getTargetFromEvent : function(e){
18678         return Roo.dd.Registry.getTargetFromEvent(e);
18679     },
18680
18681     /**
18682      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18683      * that it has registered.  This method has no default implementation and should be overridden to provide
18684      * node-specific processing if necessary.
18685      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18686      * {@link #getTargetFromEvent} for this node)
18687      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18688      * @param {Event} e The event
18689      * @param {Object} data An object containing arbitrary data supplied by the drag source
18690      */
18691     onNodeEnter : function(n, dd, e, data){
18692         
18693     },
18694
18695     /**
18696      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18697      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18698      * overridden to provide the proper feedback.
18699      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18700      * {@link #getTargetFromEvent} for this node)
18701      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18702      * @param {Event} e The event
18703      * @param {Object} data An object containing arbitrary data supplied by the drag source
18704      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18705      * underlying {@link Roo.dd.StatusProxy} can be updated
18706      */
18707     onNodeOver : function(n, dd, e, data){
18708         return this.dropAllowed;
18709     },
18710
18711     /**
18712      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18713      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18714      * node-specific processing if necessary.
18715      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18716      * {@link #getTargetFromEvent} for this node)
18717      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18718      * @param {Event} e The event
18719      * @param {Object} data An object containing arbitrary data supplied by the drag source
18720      */
18721     onNodeOut : function(n, dd, e, data){
18722         
18723     },
18724
18725     /**
18726      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18727      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18728      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18729      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18730      * {@link #getTargetFromEvent} for this node)
18731      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18732      * @param {Event} e The event
18733      * @param {Object} data An object containing arbitrary data supplied by the drag source
18734      * @return {Boolean} True if the drop was valid, else false
18735      */
18736     onNodeDrop : function(n, dd, e, data){
18737         return false;
18738     },
18739
18740     /**
18741      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18742      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18743      * it should be overridden to provide the proper feedback if necessary.
18744      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18745      * @param {Event} e The event
18746      * @param {Object} data An object containing arbitrary data supplied by the drag source
18747      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18748      * underlying {@link Roo.dd.StatusProxy} can be updated
18749      */
18750     onContainerOver : function(dd, e, data){
18751         return this.dropNotAllowed;
18752     },
18753
18754     /**
18755      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18756      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18757      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18758      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18759      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18760      * @param {Event} e The event
18761      * @param {Object} data An object containing arbitrary data supplied by the drag source
18762      * @return {Boolean} True if the drop was valid, else false
18763      */
18764     onContainerDrop : function(dd, e, data){
18765         return false;
18766     },
18767
18768     /**
18769      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18770      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18771      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18772      * you should override this method and provide a custom implementation.
18773      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18774      * @param {Event} e The event
18775      * @param {Object} data An object containing arbitrary data supplied by the drag source
18776      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18777      * underlying {@link Roo.dd.StatusProxy} can be updated
18778      */
18779     notifyEnter : function(dd, e, data){
18780         return this.dropNotAllowed;
18781     },
18782
18783     /**
18784      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18785      * This method will be called on every mouse movement while the drag source is over the drop zone.
18786      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18787      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18788      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18789      * registered node, it will call {@link #onContainerOver}.
18790      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18791      * @param {Event} e The event
18792      * @param {Object} data An object containing arbitrary data supplied by the drag source
18793      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18794      * underlying {@link Roo.dd.StatusProxy} can be updated
18795      */
18796     notifyOver : function(dd, e, data){
18797         var n = this.getTargetFromEvent(e);
18798         if(!n){ // not over valid drop target
18799             if(this.lastOverNode){
18800                 this.onNodeOut(this.lastOverNode, dd, e, data);
18801                 this.lastOverNode = null;
18802             }
18803             return this.onContainerOver(dd, e, data);
18804         }
18805         if(this.lastOverNode != n){
18806             if(this.lastOverNode){
18807                 this.onNodeOut(this.lastOverNode, dd, e, data);
18808             }
18809             this.onNodeEnter(n, dd, e, data);
18810             this.lastOverNode = n;
18811         }
18812         return this.onNodeOver(n, dd, e, data);
18813     },
18814
18815     /**
18816      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18817      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18818      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18819      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18820      * @param {Event} e The event
18821      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18822      */
18823     notifyOut : function(dd, e, data){
18824         if(this.lastOverNode){
18825             this.onNodeOut(this.lastOverNode, dd, e, data);
18826             this.lastOverNode = null;
18827         }
18828     },
18829
18830     /**
18831      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18832      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18833      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18834      * otherwise it will call {@link #onContainerDrop}.
18835      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18836      * @param {Event} e The event
18837      * @param {Object} data An object containing arbitrary data supplied by the drag source
18838      * @return {Boolean} True if the drop was valid, else false
18839      */
18840     notifyDrop : function(dd, e, data){
18841         if(this.lastOverNode){
18842             this.onNodeOut(this.lastOverNode, dd, e, data);
18843             this.lastOverNode = null;
18844         }
18845         var n = this.getTargetFromEvent(e);
18846         return n ?
18847             this.onNodeDrop(n, dd, e, data) :
18848             this.onContainerDrop(dd, e, data);
18849     },
18850
18851     // private
18852     triggerCacheRefresh : function(){
18853         Roo.dd.DDM.refreshCache(this.groups);
18854     }  
18855 });/*
18856  * Based on:
18857  * Ext JS Library 1.1.1
18858  * Copyright(c) 2006-2007, Ext JS, LLC.
18859  *
18860  * Originally Released Under LGPL - original licence link has changed is not relivant.
18861  *
18862  * Fork - LGPL
18863  * <script type="text/javascript">
18864  */
18865
18866
18867 /**
18868  * @class Roo.data.SortTypes
18869  * @singleton
18870  * Defines the default sorting (casting?) comparison functions used when sorting data.
18871  */
18872 Roo.data.SortTypes = {
18873     /**
18874      * Default sort that does nothing
18875      * @param {Mixed} s The value being converted
18876      * @return {Mixed} The comparison value
18877      */
18878     none : function(s){
18879         return s;
18880     },
18881     
18882     /**
18883      * The regular expression used to strip tags
18884      * @type {RegExp}
18885      * @property
18886      */
18887     stripTagsRE : /<\/?[^>]+>/gi,
18888     
18889     /**
18890      * Strips all HTML tags to sort on text only
18891      * @param {Mixed} s The value being converted
18892      * @return {String} The comparison value
18893      */
18894     asText : function(s){
18895         return String(s).replace(this.stripTagsRE, "");
18896     },
18897     
18898     /**
18899      * Strips all HTML tags to sort on text only - Case insensitive
18900      * @param {Mixed} s The value being converted
18901      * @return {String} The comparison value
18902      */
18903     asUCText : function(s){
18904         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18905     },
18906     
18907     /**
18908      * Case insensitive string
18909      * @param {Mixed} s The value being converted
18910      * @return {String} The comparison value
18911      */
18912     asUCString : function(s) {
18913         return String(s).toUpperCase();
18914     },
18915     
18916     /**
18917      * Date sorting
18918      * @param {Mixed} s The value being converted
18919      * @return {Number} The comparison value
18920      */
18921     asDate : function(s) {
18922         if(!s){
18923             return 0;
18924         }
18925         if(s instanceof Date){
18926             return s.getTime();
18927         }
18928         return Date.parse(String(s));
18929     },
18930     
18931     /**
18932      * Float sorting
18933      * @param {Mixed} s The value being converted
18934      * @return {Float} The comparison value
18935      */
18936     asFloat : function(s) {
18937         var val = parseFloat(String(s).replace(/,/g, ""));
18938         if(isNaN(val)) val = 0;
18939         return val;
18940     },
18941     
18942     /**
18943      * Integer sorting
18944      * @param {Mixed} s The value being converted
18945      * @return {Number} The comparison value
18946      */
18947     asInt : function(s) {
18948         var val = parseInt(String(s).replace(/,/g, ""));
18949         if(isNaN(val)) val = 0;
18950         return val;
18951     }
18952 };/*
18953  * Based on:
18954  * Ext JS Library 1.1.1
18955  * Copyright(c) 2006-2007, Ext JS, LLC.
18956  *
18957  * Originally Released Under LGPL - original licence link has changed is not relivant.
18958  *
18959  * Fork - LGPL
18960  * <script type="text/javascript">
18961  */
18962
18963 /**
18964 * @class Roo.data.Record
18965  * Instances of this class encapsulate both record <em>definition</em> information, and record
18966  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18967  * to access Records cached in an {@link Roo.data.Store} object.<br>
18968  * <p>
18969  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18970  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18971  * objects.<br>
18972  * <p>
18973  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18974  * @constructor
18975  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18976  * {@link #create}. The parameters are the same.
18977  * @param {Array} data An associative Array of data values keyed by the field name.
18978  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18979  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18980  * not specified an integer id is generated.
18981  */
18982 Roo.data.Record = function(data, id){
18983     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18984     this.data = data;
18985 };
18986
18987 /**
18988  * Generate a constructor for a specific record layout.
18989  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18990  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18991  * Each field definition object may contain the following properties: <ul>
18992  * <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,
18993  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18994  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18995  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18996  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18997  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18998  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18999  * this may be omitted.</p></li>
19000  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19001  * <ul><li>auto (Default, implies no conversion)</li>
19002  * <li>string</li>
19003  * <li>int</li>
19004  * <li>float</li>
19005  * <li>boolean</li>
19006  * <li>date</li></ul></p></li>
19007  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19008  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19009  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19010  * by the Reader into an object that will be stored in the Record. It is passed the
19011  * following parameters:<ul>
19012  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19013  * </ul></p></li>
19014  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19015  * </ul>
19016  * <br>usage:<br><pre><code>
19017 var TopicRecord = Roo.data.Record.create(
19018     {name: 'title', mapping: 'topic_title'},
19019     {name: 'author', mapping: 'username'},
19020     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19021     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19022     {name: 'lastPoster', mapping: 'user2'},
19023     {name: 'excerpt', mapping: 'post_text'}
19024 );
19025
19026 var myNewRecord = new TopicRecord({
19027     title: 'Do my job please',
19028     author: 'noobie',
19029     totalPosts: 1,
19030     lastPost: new Date(),
19031     lastPoster: 'Animal',
19032     excerpt: 'No way dude!'
19033 });
19034 myStore.add(myNewRecord);
19035 </code></pre>
19036  * @method create
19037  * @static
19038  */
19039 Roo.data.Record.create = function(o){
19040     var f = function(){
19041         f.superclass.constructor.apply(this, arguments);
19042     };
19043     Roo.extend(f, Roo.data.Record);
19044     var p = f.prototype;
19045     p.fields = new Roo.util.MixedCollection(false, function(field){
19046         return field.name;
19047     });
19048     for(var i = 0, len = o.length; i < len; i++){
19049         p.fields.add(new Roo.data.Field(o[i]));
19050     }
19051     f.getField = function(name){
19052         return p.fields.get(name);  
19053     };
19054     return f;
19055 };
19056
19057 Roo.data.Record.AUTO_ID = 1000;
19058 Roo.data.Record.EDIT = 'edit';
19059 Roo.data.Record.REJECT = 'reject';
19060 Roo.data.Record.COMMIT = 'commit';
19061
19062 Roo.data.Record.prototype = {
19063     /**
19064      * Readonly flag - true if this record has been modified.
19065      * @type Boolean
19066      */
19067     dirty : false,
19068     editing : false,
19069     error: null,
19070     modified: null,
19071
19072     // private
19073     join : function(store){
19074         this.store = store;
19075     },
19076
19077     /**
19078      * Set the named field to the specified value.
19079      * @param {String} name The name of the field to set.
19080      * @param {Object} value The value to set the field to.
19081      */
19082     set : function(name, value){
19083         if(this.data[name] == value){
19084             return;
19085         }
19086         this.dirty = true;
19087         if(!this.modified){
19088             this.modified = {};
19089         }
19090         if(typeof this.modified[name] == 'undefined'){
19091             this.modified[name] = this.data[name];
19092         }
19093         this.data[name] = value;
19094         if(!this.editing && this.store){
19095             this.store.afterEdit(this);
19096         }       
19097     },
19098
19099     /**
19100      * Get the value of the named field.
19101      * @param {String} name The name of the field to get the value of.
19102      * @return {Object} The value of the field.
19103      */
19104     get : function(name){
19105         return this.data[name]; 
19106     },
19107
19108     // private
19109     beginEdit : function(){
19110         this.editing = true;
19111         this.modified = {}; 
19112     },
19113
19114     // private
19115     cancelEdit : function(){
19116         this.editing = false;
19117         delete this.modified;
19118     },
19119
19120     // private
19121     endEdit : function(){
19122         this.editing = false;
19123         if(this.dirty && this.store){
19124             this.store.afterEdit(this);
19125         }
19126     },
19127
19128     /**
19129      * Usually called by the {@link Roo.data.Store} which owns the Record.
19130      * Rejects all changes made to the Record since either creation, or the last commit operation.
19131      * Modified fields are reverted to their original values.
19132      * <p>
19133      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19134      * of reject operations.
19135      */
19136     reject : function(){
19137         var m = this.modified;
19138         for(var n in m){
19139             if(typeof m[n] != "function"){
19140                 this.data[n] = m[n];
19141             }
19142         }
19143         this.dirty = false;
19144         delete this.modified;
19145         this.editing = false;
19146         if(this.store){
19147             this.store.afterReject(this);
19148         }
19149     },
19150
19151     /**
19152      * Usually called by the {@link Roo.data.Store} which owns the Record.
19153      * Commits all changes made to the Record since either creation, or the last commit operation.
19154      * <p>
19155      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19156      * of commit operations.
19157      */
19158     commit : function(){
19159         this.dirty = false;
19160         delete this.modified;
19161         this.editing = false;
19162         if(this.store){
19163             this.store.afterCommit(this);
19164         }
19165     },
19166
19167     // private
19168     hasError : function(){
19169         return this.error != null;
19170     },
19171
19172     // private
19173     clearError : function(){
19174         this.error = null;
19175     },
19176
19177     /**
19178      * Creates a copy of this record.
19179      * @param {String} id (optional) A new record id if you don't want to use this record's id
19180      * @return {Record}
19181      */
19182     copy : function(newId) {
19183         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19184     }
19185 };/*
19186  * Based on:
19187  * Ext JS Library 1.1.1
19188  * Copyright(c) 2006-2007, Ext JS, LLC.
19189  *
19190  * Originally Released Under LGPL - original licence link has changed is not relivant.
19191  *
19192  * Fork - LGPL
19193  * <script type="text/javascript">
19194  */
19195
19196
19197
19198 /**
19199  * @class Roo.data.Store
19200  * @extends Roo.util.Observable
19201  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19202  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19203  * <p>
19204  * 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
19205  * has no knowledge of the format of the data returned by the Proxy.<br>
19206  * <p>
19207  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19208  * instances from the data object. These records are cached and made available through accessor functions.
19209  * @constructor
19210  * Creates a new Store.
19211  * @param {Object} config A config object containing the objects needed for the Store to access data,
19212  * and read the data into Records.
19213  */
19214 Roo.data.Store = function(config){
19215     this.data = new Roo.util.MixedCollection(false);
19216     this.data.getKey = function(o){
19217         return o.id;
19218     };
19219     this.baseParams = {};
19220     // private
19221     this.paramNames = {
19222         "start" : "start",
19223         "limit" : "limit",
19224         "sort" : "sort",
19225         "dir" : "dir",
19226         "multisort" : "_multisort"
19227     };
19228
19229     if(config && config.data){
19230         this.inlineData = config.data;
19231         delete config.data;
19232     }
19233
19234     Roo.apply(this, config);
19235     
19236     if(this.reader){ // reader passed
19237         this.reader = Roo.factory(this.reader, Roo.data);
19238         this.reader.xmodule = this.xmodule || false;
19239         if(!this.recordType){
19240             this.recordType = this.reader.recordType;
19241         }
19242         if(this.reader.onMetaChange){
19243             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19244         }
19245     }
19246
19247     if(this.recordType){
19248         this.fields = this.recordType.prototype.fields;
19249     }
19250     this.modified = [];
19251
19252     this.addEvents({
19253         /**
19254          * @event datachanged
19255          * Fires when the data cache has changed, and a widget which is using this Store
19256          * as a Record cache should refresh its view.
19257          * @param {Store} this
19258          */
19259         datachanged : true,
19260         /**
19261          * @event metachange
19262          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19263          * @param {Store} this
19264          * @param {Object} meta The JSON metadata
19265          */
19266         metachange : true,
19267         /**
19268          * @event add
19269          * Fires when Records have been added to the Store
19270          * @param {Store} this
19271          * @param {Roo.data.Record[]} records The array of Records added
19272          * @param {Number} index The index at which the record(s) were added
19273          */
19274         add : true,
19275         /**
19276          * @event remove
19277          * Fires when a Record has been removed from the Store
19278          * @param {Store} this
19279          * @param {Roo.data.Record} record The Record that was removed
19280          * @param {Number} index The index at which the record was removed
19281          */
19282         remove : true,
19283         /**
19284          * @event update
19285          * Fires when a Record has been updated
19286          * @param {Store} this
19287          * @param {Roo.data.Record} record The Record that was updated
19288          * @param {String} operation The update operation being performed.  Value may be one of:
19289          * <pre><code>
19290  Roo.data.Record.EDIT
19291  Roo.data.Record.REJECT
19292  Roo.data.Record.COMMIT
19293          * </code></pre>
19294          */
19295         update : true,
19296         /**
19297          * @event clear
19298          * Fires when the data cache has been cleared.
19299          * @param {Store} this
19300          */
19301         clear : true,
19302         /**
19303          * @event beforeload
19304          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19305          * the load action will be canceled.
19306          * @param {Store} this
19307          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19308          */
19309         beforeload : true,
19310         /**
19311          * @event load
19312          * Fires after a new set of Records has been loaded.
19313          * @param {Store} this
19314          * @param {Roo.data.Record[]} records The Records that were loaded
19315          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19316          */
19317         load : true,
19318         /**
19319          * @event loadexception
19320          * Fires if an exception occurs in the Proxy during loading.
19321          * Called with the signature of the Proxy's "loadexception" event.
19322          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19323          * 
19324          * @param {Proxy} 
19325          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19326          * @param {Object} load options 
19327          * @param {Object} jsonData from your request (normally this contains the Exception)
19328          */
19329         loadexception : true
19330     });
19331     
19332     if(this.proxy){
19333         this.proxy = Roo.factory(this.proxy, Roo.data);
19334         this.proxy.xmodule = this.xmodule || false;
19335         this.relayEvents(this.proxy,  ["loadexception"]);
19336     }
19337     this.sortToggle = {};
19338     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19339
19340     Roo.data.Store.superclass.constructor.call(this);
19341
19342     if(this.inlineData){
19343         this.loadData(this.inlineData);
19344         delete this.inlineData;
19345     }
19346 };
19347 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19348      /**
19349     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19350     * without a remote query - used by combo/forms at present.
19351     */
19352     
19353     /**
19354     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19355     */
19356     /**
19357     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19358     */
19359     /**
19360     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19361     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19362     */
19363     /**
19364     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19365     * on any HTTP request
19366     */
19367     /**
19368     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19369     */
19370     /**
19371     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19372     */
19373     multiSort: false,
19374     /**
19375     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19376     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19377     */
19378     remoteSort : false,
19379
19380     /**
19381     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19382      * loaded or when a record is removed. (defaults to false).
19383     */
19384     pruneModifiedRecords : false,
19385
19386     // private
19387     lastOptions : null,
19388
19389     /**
19390      * Add Records to the Store and fires the add event.
19391      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19392      */
19393     add : function(records){
19394         records = [].concat(records);
19395         for(var i = 0, len = records.length; i < len; i++){
19396             records[i].join(this);
19397         }
19398         var index = this.data.length;
19399         this.data.addAll(records);
19400         this.fireEvent("add", this, records, index);
19401     },
19402
19403     /**
19404      * Remove a Record from the Store and fires the remove event.
19405      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19406      */
19407     remove : function(record){
19408         var index = this.data.indexOf(record);
19409         this.data.removeAt(index);
19410         if(this.pruneModifiedRecords){
19411             this.modified.remove(record);
19412         }
19413         this.fireEvent("remove", this, record, index);
19414     },
19415
19416     /**
19417      * Remove all Records from the Store and fires the clear event.
19418      */
19419     removeAll : function(){
19420         this.data.clear();
19421         if(this.pruneModifiedRecords){
19422             this.modified = [];
19423         }
19424         this.fireEvent("clear", this);
19425     },
19426
19427     /**
19428      * Inserts Records to the Store at the given index and fires the add event.
19429      * @param {Number} index The start index at which to insert the passed Records.
19430      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19431      */
19432     insert : function(index, records){
19433         records = [].concat(records);
19434         for(var i = 0, len = records.length; i < len; i++){
19435             this.data.insert(index, records[i]);
19436             records[i].join(this);
19437         }
19438         this.fireEvent("add", this, records, index);
19439     },
19440
19441     /**
19442      * Get the index within the cache of the passed Record.
19443      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19444      * @return {Number} The index of the passed Record. Returns -1 if not found.
19445      */
19446     indexOf : function(record){
19447         return this.data.indexOf(record);
19448     },
19449
19450     /**
19451      * Get the index within the cache of the Record with the passed id.
19452      * @param {String} id The id of the Record to find.
19453      * @return {Number} The index of the Record. Returns -1 if not found.
19454      */
19455     indexOfId : function(id){
19456         return this.data.indexOfKey(id);
19457     },
19458
19459     /**
19460      * Get the Record with the specified id.
19461      * @param {String} id The id of the Record to find.
19462      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19463      */
19464     getById : function(id){
19465         return this.data.key(id);
19466     },
19467
19468     /**
19469      * Get the Record at the specified index.
19470      * @param {Number} index The index of the Record to find.
19471      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19472      */
19473     getAt : function(index){
19474         return this.data.itemAt(index);
19475     },
19476
19477     /**
19478      * Returns a range of Records between specified indices.
19479      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19480      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19481      * @return {Roo.data.Record[]} An array of Records
19482      */
19483     getRange : function(start, end){
19484         return this.data.getRange(start, end);
19485     },
19486
19487     // private
19488     storeOptions : function(o){
19489         o = Roo.apply({}, o);
19490         delete o.callback;
19491         delete o.scope;
19492         this.lastOptions = o;
19493     },
19494
19495     /**
19496      * Loads the Record cache from the configured Proxy using the configured Reader.
19497      * <p>
19498      * If using remote paging, then the first load call must specify the <em>start</em>
19499      * and <em>limit</em> properties in the options.params property to establish the initial
19500      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19501      * <p>
19502      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19503      * and this call will return before the new data has been loaded. Perform any post-processing
19504      * in a callback function, or in a "load" event handler.</strong>
19505      * <p>
19506      * @param {Object} options An object containing properties which control loading options:<ul>
19507      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19508      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19509      * passed the following arguments:<ul>
19510      * <li>r : Roo.data.Record[]</li>
19511      * <li>options: Options object from the load call</li>
19512      * <li>success: Boolean success indicator</li></ul></li>
19513      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19514      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19515      * </ul>
19516      */
19517     load : function(options){
19518         options = options || {};
19519         if(this.fireEvent("beforeload", this, options) !== false){
19520             this.storeOptions(options);
19521             var p = Roo.apply(options.params || {}, this.baseParams);
19522             // if meta was not loaded from remote source.. try requesting it.
19523             if (!this.reader.metaFromRemote) {
19524                 p._requestMeta = 1;
19525             }
19526             if(this.sortInfo && this.remoteSort){
19527                 var pn = this.paramNames;
19528                 p[pn["sort"]] = this.sortInfo.field;
19529                 p[pn["dir"]] = this.sortInfo.direction;
19530             }
19531             if (this.multiSort) {
19532                 var pn = this.paramNames;
19533                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19534             }
19535             
19536             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19537         }
19538     },
19539
19540     /**
19541      * Reloads the Record cache from the configured Proxy using the configured Reader and
19542      * the options from the last load operation performed.
19543      * @param {Object} options (optional) An object containing properties which may override the options
19544      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19545      * the most recently used options are reused).
19546      */
19547     reload : function(options){
19548         this.load(Roo.applyIf(options||{}, this.lastOptions));
19549     },
19550
19551     // private
19552     // Called as a callback by the Reader during a load operation.
19553     loadRecords : function(o, options, success){
19554         if(!o || success === false){
19555             if(success !== false){
19556                 this.fireEvent("load", this, [], options);
19557             }
19558             if(options.callback){
19559                 options.callback.call(options.scope || this, [], options, false);
19560             }
19561             return;
19562         }
19563         // if data returned failure - throw an exception.
19564         if (o.success === false) {
19565             // show a message if no listener is registered.
19566             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19567                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19568             }
19569             // loadmask wil be hooked into this..
19570             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19571             return;
19572         }
19573         var r = o.records, t = o.totalRecords || r.length;
19574         if(!options || options.add !== true){
19575             if(this.pruneModifiedRecords){
19576                 this.modified = [];
19577             }
19578             for(var i = 0, len = r.length; i < len; i++){
19579                 r[i].join(this);
19580             }
19581             if(this.snapshot){
19582                 this.data = this.snapshot;
19583                 delete this.snapshot;
19584             }
19585             this.data.clear();
19586             this.data.addAll(r);
19587             this.totalLength = t;
19588             this.applySort();
19589             this.fireEvent("datachanged", this);
19590         }else{
19591             this.totalLength = Math.max(t, this.data.length+r.length);
19592             this.add(r);
19593         }
19594         this.fireEvent("load", this, r, options);
19595         if(options.callback){
19596             options.callback.call(options.scope || this, r, options, true);
19597         }
19598     },
19599
19600
19601     /**
19602      * Loads data from a passed data block. A Reader which understands the format of the data
19603      * must have been configured in the constructor.
19604      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19605      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19606      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19607      */
19608     loadData : function(o, append){
19609         var r = this.reader.readRecords(o);
19610         this.loadRecords(r, {add: append}, true);
19611     },
19612
19613     /**
19614      * Gets the number of cached records.
19615      * <p>
19616      * <em>If using paging, this may not be the total size of the dataset. If the data object
19617      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19618      * the data set size</em>
19619      */
19620     getCount : function(){
19621         return this.data.length || 0;
19622     },
19623
19624     /**
19625      * Gets the total number of records in the dataset as returned by the server.
19626      * <p>
19627      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19628      * the dataset size</em>
19629      */
19630     getTotalCount : function(){
19631         return this.totalLength || 0;
19632     },
19633
19634     /**
19635      * Returns the sort state of the Store as an object with two properties:
19636      * <pre><code>
19637  field {String} The name of the field by which the Records are sorted
19638  direction {String} The sort order, "ASC" or "DESC"
19639      * </code></pre>
19640      */
19641     getSortState : function(){
19642         return this.sortInfo;
19643     },
19644
19645     // private
19646     applySort : function(){
19647         if(this.sortInfo && !this.remoteSort){
19648             var s = this.sortInfo, f = s.field;
19649             var st = this.fields.get(f).sortType;
19650             var fn = function(r1, r2){
19651                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19652                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19653             };
19654             this.data.sort(s.direction, fn);
19655             if(this.snapshot && this.snapshot != this.data){
19656                 this.snapshot.sort(s.direction, fn);
19657             }
19658         }
19659     },
19660
19661     /**
19662      * Sets the default sort column and order to be used by the next load operation.
19663      * @param {String} fieldName The name of the field to sort by.
19664      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19665      */
19666     setDefaultSort : function(field, dir){
19667         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19668     },
19669
19670     /**
19671      * Sort the Records.
19672      * If remote sorting is used, the sort is performed on the server, and the cache is
19673      * reloaded. If local sorting is used, the cache is sorted internally.
19674      * @param {String} fieldName The name of the field to sort by.
19675      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19676      */
19677     sort : function(fieldName, dir){
19678         var f = this.fields.get(fieldName);
19679         if(!dir){
19680             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19681             
19682             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19683                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19684             }else{
19685                 dir = f.sortDir;
19686             }
19687         }
19688         this.sortToggle[f.name] = dir;
19689         this.sortInfo = {field: f.name, direction: dir};
19690         if(!this.remoteSort){
19691             this.applySort();
19692             this.fireEvent("datachanged", this);
19693         }else{
19694             this.load(this.lastOptions);
19695         }
19696     },
19697
19698     /**
19699      * Calls the specified function for each of the Records in the cache.
19700      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19701      * Returning <em>false</em> aborts and exits the iteration.
19702      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19703      */
19704     each : function(fn, scope){
19705         this.data.each(fn, scope);
19706     },
19707
19708     /**
19709      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19710      * (e.g., during paging).
19711      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19712      */
19713     getModifiedRecords : function(){
19714         return this.modified;
19715     },
19716
19717     // private
19718     createFilterFn : function(property, value, anyMatch){
19719         if(!value.exec){ // not a regex
19720             value = String(value);
19721             if(value.length == 0){
19722                 return false;
19723             }
19724             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19725         }
19726         return function(r){
19727             return value.test(r.data[property]);
19728         };
19729     },
19730
19731     /**
19732      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19733      * @param {String} property A field on your records
19734      * @param {Number} start The record index to start at (defaults to 0)
19735      * @param {Number} end The last record index to include (defaults to length - 1)
19736      * @return {Number} The sum
19737      */
19738     sum : function(property, start, end){
19739         var rs = this.data.items, v = 0;
19740         start = start || 0;
19741         end = (end || end === 0) ? end : rs.length-1;
19742
19743         for(var i = start; i <= end; i++){
19744             v += (rs[i].data[property] || 0);
19745         }
19746         return v;
19747     },
19748
19749     /**
19750      * Filter the records by a specified property.
19751      * @param {String} field A field on your records
19752      * @param {String/RegExp} value Either a string that the field
19753      * should start with or a RegExp to test against the field
19754      * @param {Boolean} anyMatch True to match any part not just the beginning
19755      */
19756     filter : function(property, value, anyMatch){
19757         var fn = this.createFilterFn(property, value, anyMatch);
19758         return fn ? this.filterBy(fn) : this.clearFilter();
19759     },
19760
19761     /**
19762      * Filter by a function. The specified function will be called with each
19763      * record in this data source. If the function returns true the record is included,
19764      * otherwise it is filtered.
19765      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19766      * @param {Object} scope (optional) The scope of the function (defaults to this)
19767      */
19768     filterBy : function(fn, scope){
19769         this.snapshot = this.snapshot || this.data;
19770         this.data = this.queryBy(fn, scope||this);
19771         this.fireEvent("datachanged", this);
19772     },
19773
19774     /**
19775      * Query the records by a specified property.
19776      * @param {String} field A field on your records
19777      * @param {String/RegExp} value Either a string that the field
19778      * should start with or a RegExp to test against the field
19779      * @param {Boolean} anyMatch True to match any part not just the beginning
19780      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19781      */
19782     query : function(property, value, anyMatch){
19783         var fn = this.createFilterFn(property, value, anyMatch);
19784         return fn ? this.queryBy(fn) : this.data.clone();
19785     },
19786
19787     /**
19788      * Query by a function. The specified function will be called with each
19789      * record in this data source. If the function returns true the record is included
19790      * in the results.
19791      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19792      * @param {Object} scope (optional) The scope of the function (defaults to this)
19793       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19794      **/
19795     queryBy : function(fn, scope){
19796         var data = this.snapshot || this.data;
19797         return data.filterBy(fn, scope||this);
19798     },
19799
19800     /**
19801      * Collects unique values for a particular dataIndex from this store.
19802      * @param {String} dataIndex The property to collect
19803      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19804      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19805      * @return {Array} An array of the unique values
19806      **/
19807     collect : function(dataIndex, allowNull, bypassFilter){
19808         var d = (bypassFilter === true && this.snapshot) ?
19809                 this.snapshot.items : this.data.items;
19810         var v, sv, r = [], l = {};
19811         for(var i = 0, len = d.length; i < len; i++){
19812             v = d[i].data[dataIndex];
19813             sv = String(v);
19814             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19815                 l[sv] = true;
19816                 r[r.length] = v;
19817             }
19818         }
19819         return r;
19820     },
19821
19822     /**
19823      * Revert to a view of the Record cache with no filtering applied.
19824      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19825      */
19826     clearFilter : function(suppressEvent){
19827         if(this.snapshot && this.snapshot != this.data){
19828             this.data = this.snapshot;
19829             delete this.snapshot;
19830             if(suppressEvent !== true){
19831                 this.fireEvent("datachanged", this);
19832             }
19833         }
19834     },
19835
19836     // private
19837     afterEdit : function(record){
19838         if(this.modified.indexOf(record) == -1){
19839             this.modified.push(record);
19840         }
19841         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19842     },
19843     
19844     // private
19845     afterReject : function(record){
19846         this.modified.remove(record);
19847         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19848     },
19849
19850     // private
19851     afterCommit : function(record){
19852         this.modified.remove(record);
19853         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19854     },
19855
19856     /**
19857      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19858      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19859      */
19860     commitChanges : function(){
19861         var m = this.modified.slice(0);
19862         this.modified = [];
19863         for(var i = 0, len = m.length; i < len; i++){
19864             m[i].commit();
19865         }
19866     },
19867
19868     /**
19869      * Cancel outstanding changes on all changed records.
19870      */
19871     rejectChanges : function(){
19872         var m = this.modified.slice(0);
19873         this.modified = [];
19874         for(var i = 0, len = m.length; i < len; i++){
19875             m[i].reject();
19876         }
19877     },
19878
19879     onMetaChange : function(meta, rtype, o){
19880         this.recordType = rtype;
19881         this.fields = rtype.prototype.fields;
19882         delete this.snapshot;
19883         this.sortInfo = meta.sortInfo || this.sortInfo;
19884         this.modified = [];
19885         this.fireEvent('metachange', this, this.reader.meta);
19886     }
19887 });/*
19888  * Based on:
19889  * Ext JS Library 1.1.1
19890  * Copyright(c) 2006-2007, Ext JS, LLC.
19891  *
19892  * Originally Released Under LGPL - original licence link has changed is not relivant.
19893  *
19894  * Fork - LGPL
19895  * <script type="text/javascript">
19896  */
19897
19898 /**
19899  * @class Roo.data.SimpleStore
19900  * @extends Roo.data.Store
19901  * Small helper class to make creating Stores from Array data easier.
19902  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19903  * @cfg {Array} fields An array of field definition objects, or field name strings.
19904  * @cfg {Array} data The multi-dimensional array of data
19905  * @constructor
19906  * @param {Object} config
19907  */
19908 Roo.data.SimpleStore = function(config){
19909     Roo.data.SimpleStore.superclass.constructor.call(this, {
19910         isLocal : true,
19911         reader: new Roo.data.ArrayReader({
19912                 id: config.id
19913             },
19914             Roo.data.Record.create(config.fields)
19915         ),
19916         proxy : new Roo.data.MemoryProxy(config.data)
19917     });
19918     this.load();
19919 };
19920 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19921  * Based on:
19922  * Ext JS Library 1.1.1
19923  * Copyright(c) 2006-2007, Ext JS, LLC.
19924  *
19925  * Originally Released Under LGPL - original licence link has changed is not relivant.
19926  *
19927  * Fork - LGPL
19928  * <script type="text/javascript">
19929  */
19930
19931 /**
19932 /**
19933  * @extends Roo.data.Store
19934  * @class Roo.data.JsonStore
19935  * Small helper class to make creating Stores for JSON data easier. <br/>
19936 <pre><code>
19937 var store = new Roo.data.JsonStore({
19938     url: 'get-images.php',
19939     root: 'images',
19940     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19941 });
19942 </code></pre>
19943  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19944  * JsonReader and HttpProxy (unless inline data is provided).</b>
19945  * @cfg {Array} fields An array of field definition objects, or field name strings.
19946  * @constructor
19947  * @param {Object} config
19948  */
19949 Roo.data.JsonStore = function(c){
19950     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19951         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19952         reader: new Roo.data.JsonReader(c, c.fields)
19953     }));
19954 };
19955 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19956  * Based on:
19957  * Ext JS Library 1.1.1
19958  * Copyright(c) 2006-2007, Ext JS, LLC.
19959  *
19960  * Originally Released Under LGPL - original licence link has changed is not relivant.
19961  *
19962  * Fork - LGPL
19963  * <script type="text/javascript">
19964  */
19965
19966  
19967 Roo.data.Field = function(config){
19968     if(typeof config == "string"){
19969         config = {name: config};
19970     }
19971     Roo.apply(this, config);
19972     
19973     if(!this.type){
19974         this.type = "auto";
19975     }
19976     
19977     var st = Roo.data.SortTypes;
19978     // named sortTypes are supported, here we look them up
19979     if(typeof this.sortType == "string"){
19980         this.sortType = st[this.sortType];
19981     }
19982     
19983     // set default sortType for strings and dates
19984     if(!this.sortType){
19985         switch(this.type){
19986             case "string":
19987                 this.sortType = st.asUCString;
19988                 break;
19989             case "date":
19990                 this.sortType = st.asDate;
19991                 break;
19992             default:
19993                 this.sortType = st.none;
19994         }
19995     }
19996
19997     // define once
19998     var stripRe = /[\$,%]/g;
19999
20000     // prebuilt conversion function for this field, instead of
20001     // switching every time we're reading a value
20002     if(!this.convert){
20003         var cv, dateFormat = this.dateFormat;
20004         switch(this.type){
20005             case "":
20006             case "auto":
20007             case undefined:
20008                 cv = function(v){ return v; };
20009                 break;
20010             case "string":
20011                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20012                 break;
20013             case "int":
20014                 cv = function(v){
20015                     return v !== undefined && v !== null && v !== '' ?
20016                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20017                     };
20018                 break;
20019             case "float":
20020                 cv = function(v){
20021                     return v !== undefined && v !== null && v !== '' ?
20022                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20023                     };
20024                 break;
20025             case "bool":
20026             case "boolean":
20027                 cv = function(v){ return v === true || v === "true" || v == 1; };
20028                 break;
20029             case "date":
20030                 cv = function(v){
20031                     if(!v){
20032                         return '';
20033                     }
20034                     if(v instanceof Date){
20035                         return v;
20036                     }
20037                     if(dateFormat){
20038                         if(dateFormat == "timestamp"){
20039                             return new Date(v*1000);
20040                         }
20041                         return Date.parseDate(v, dateFormat);
20042                     }
20043                     var parsed = Date.parse(v);
20044                     return parsed ? new Date(parsed) : null;
20045                 };
20046              break;
20047             
20048         }
20049         this.convert = cv;
20050     }
20051 };
20052
20053 Roo.data.Field.prototype = {
20054     dateFormat: null,
20055     defaultValue: "",
20056     mapping: null,
20057     sortType : null,
20058     sortDir : "ASC"
20059 };/*
20060  * Based on:
20061  * Ext JS Library 1.1.1
20062  * Copyright(c) 2006-2007, Ext JS, LLC.
20063  *
20064  * Originally Released Under LGPL - original licence link has changed is not relivant.
20065  *
20066  * Fork - LGPL
20067  * <script type="text/javascript">
20068  */
20069  
20070 // Base class for reading structured data from a data source.  This class is intended to be
20071 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20072
20073 /**
20074  * @class Roo.data.DataReader
20075  * Base class for reading structured data from a data source.  This class is intended to be
20076  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20077  */
20078
20079 Roo.data.DataReader = function(meta, recordType){
20080     
20081     this.meta = meta;
20082     
20083     this.recordType = recordType instanceof Array ? 
20084         Roo.data.Record.create(recordType) : recordType;
20085 };
20086
20087 Roo.data.DataReader.prototype = {
20088      /**
20089      * Create an empty record
20090      * @param {Object} data (optional) - overlay some values
20091      * @return {Roo.data.Record} record created.
20092      */
20093     newRow :  function(d) {
20094         var da =  {};
20095         this.recordType.prototype.fields.each(function(c) {
20096             switch( c.type) {
20097                 case 'int' : da[c.name] = 0; break;
20098                 case 'date' : da[c.name] = new Date(); break;
20099                 case 'float' : da[c.name] = 0.0; break;
20100                 case 'boolean' : da[c.name] = false; break;
20101                 default : da[c.name] = ""; break;
20102             }
20103             
20104         });
20105         return new this.recordType(Roo.apply(da, d));
20106     }
20107     
20108 };/*
20109  * Based on:
20110  * Ext JS Library 1.1.1
20111  * Copyright(c) 2006-2007, Ext JS, LLC.
20112  *
20113  * Originally Released Under LGPL - original licence link has changed is not relivant.
20114  *
20115  * Fork - LGPL
20116  * <script type="text/javascript">
20117  */
20118
20119 /**
20120  * @class Roo.data.DataProxy
20121  * @extends Roo.data.Observable
20122  * This class is an abstract base class for implementations which provide retrieval of
20123  * unformatted data objects.<br>
20124  * <p>
20125  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20126  * (of the appropriate type which knows how to parse the data object) to provide a block of
20127  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20128  * <p>
20129  * Custom implementations must implement the load method as described in
20130  * {@link Roo.data.HttpProxy#load}.
20131  */
20132 Roo.data.DataProxy = function(){
20133     this.addEvents({
20134         /**
20135          * @event beforeload
20136          * Fires before a network request is made to retrieve a data object.
20137          * @param {Object} This DataProxy object.
20138          * @param {Object} params The params parameter to the load function.
20139          */
20140         beforeload : true,
20141         /**
20142          * @event load
20143          * Fires before the load method's callback is called.
20144          * @param {Object} This DataProxy object.
20145          * @param {Object} o The data object.
20146          * @param {Object} arg The callback argument object passed to the load function.
20147          */
20148         load : true,
20149         /**
20150          * @event loadexception
20151          * Fires if an Exception occurs during data retrieval.
20152          * @param {Object} This DataProxy object.
20153          * @param {Object} o The data object.
20154          * @param {Object} arg The callback argument object passed to the load function.
20155          * @param {Object} e The Exception.
20156          */
20157         loadexception : true
20158     });
20159     Roo.data.DataProxy.superclass.constructor.call(this);
20160 };
20161
20162 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20163
20164     /**
20165      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20166      */
20167 /*
20168  * Based on:
20169  * Ext JS Library 1.1.1
20170  * Copyright(c) 2006-2007, Ext JS, LLC.
20171  *
20172  * Originally Released Under LGPL - original licence link has changed is not relivant.
20173  *
20174  * Fork - LGPL
20175  * <script type="text/javascript">
20176  */
20177 /**
20178  * @class Roo.data.MemoryProxy
20179  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20180  * to the Reader when its load method is called.
20181  * @constructor
20182  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20183  */
20184 Roo.data.MemoryProxy = function(data){
20185     if (data.data) {
20186         data = data.data;
20187     }
20188     Roo.data.MemoryProxy.superclass.constructor.call(this);
20189     this.data = data;
20190 };
20191
20192 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20193     /**
20194      * Load data from the requested source (in this case an in-memory
20195      * data object passed to the constructor), read the data object into
20196      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20197      * process that block using the passed callback.
20198      * @param {Object} params This parameter is not used by the MemoryProxy class.
20199      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20200      * object into a block of Roo.data.Records.
20201      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20202      * The function must be passed <ul>
20203      * <li>The Record block object</li>
20204      * <li>The "arg" argument from the load function</li>
20205      * <li>A boolean success indicator</li>
20206      * </ul>
20207      * @param {Object} scope The scope in which to call the callback
20208      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20209      */
20210     load : function(params, reader, callback, scope, arg){
20211         params = params || {};
20212         var result;
20213         try {
20214             result = reader.readRecords(this.data);
20215         }catch(e){
20216             this.fireEvent("loadexception", this, arg, null, e);
20217             callback.call(scope, null, arg, false);
20218             return;
20219         }
20220         callback.call(scope, result, arg, true);
20221     },
20222     
20223     // private
20224     update : function(params, records){
20225         
20226     }
20227 });/*
20228  * Based on:
20229  * Ext JS Library 1.1.1
20230  * Copyright(c) 2006-2007, Ext JS, LLC.
20231  *
20232  * Originally Released Under LGPL - original licence link has changed is not relivant.
20233  *
20234  * Fork - LGPL
20235  * <script type="text/javascript">
20236  */
20237 /**
20238  * @class Roo.data.HttpProxy
20239  * @extends Roo.data.DataProxy
20240  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20241  * configured to reference a certain URL.<br><br>
20242  * <p>
20243  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20244  * from which the running page was served.<br><br>
20245  * <p>
20246  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20247  * <p>
20248  * Be aware that to enable the browser to parse an XML document, the server must set
20249  * the Content-Type header in the HTTP response to "text/xml".
20250  * @constructor
20251  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20252  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20253  * will be used to make the request.
20254  */
20255 Roo.data.HttpProxy = function(conn){
20256     Roo.data.HttpProxy.superclass.constructor.call(this);
20257     // is conn a conn config or a real conn?
20258     this.conn = conn;
20259     this.useAjax = !conn || !conn.events;
20260   
20261 };
20262
20263 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20264     // thse are take from connection...
20265     
20266     /**
20267      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20268      */
20269     /**
20270      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20271      * extra parameters to each request made by this object. (defaults to undefined)
20272      */
20273     /**
20274      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20275      *  to each request made by this object. (defaults to undefined)
20276      */
20277     /**
20278      * @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)
20279      */
20280     /**
20281      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20282      */
20283      /**
20284      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20285      * @type Boolean
20286      */
20287   
20288
20289     /**
20290      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20291      * @type Boolean
20292      */
20293     /**
20294      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20295      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20296      * a finer-grained basis than the DataProxy events.
20297      */
20298     getConnection : function(){
20299         return this.useAjax ? Roo.Ajax : this.conn;
20300     },
20301
20302     /**
20303      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20304      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20305      * process that block using the passed callback.
20306      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20307      * for the request to the remote server.
20308      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20309      * object into a block of Roo.data.Records.
20310      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20311      * The function must be passed <ul>
20312      * <li>The Record block object</li>
20313      * <li>The "arg" argument from the load function</li>
20314      * <li>A boolean success indicator</li>
20315      * </ul>
20316      * @param {Object} scope The scope in which to call the callback
20317      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20318      */
20319     load : function(params, reader, callback, scope, arg){
20320         if(this.fireEvent("beforeload", this, params) !== false){
20321             var  o = {
20322                 params : params || {},
20323                 request: {
20324                     callback : callback,
20325                     scope : scope,
20326                     arg : arg
20327                 },
20328                 reader: reader,
20329                 callback : this.loadResponse,
20330                 scope: this
20331             };
20332             if(this.useAjax){
20333                 Roo.applyIf(o, this.conn);
20334                 if(this.activeRequest){
20335                     Roo.Ajax.abort(this.activeRequest);
20336                 }
20337                 this.activeRequest = Roo.Ajax.request(o);
20338             }else{
20339                 this.conn.request(o);
20340             }
20341         }else{
20342             callback.call(scope||this, null, arg, false);
20343         }
20344     },
20345
20346     // private
20347     loadResponse : function(o, success, response){
20348         delete this.activeRequest;
20349         if(!success){
20350             this.fireEvent("loadexception", this, o, response);
20351             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20352             return;
20353         }
20354         var result;
20355         try {
20356             result = o.reader.read(response);
20357         }catch(e){
20358             this.fireEvent("loadexception", this, o, response, e);
20359             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20360             return;
20361         }
20362         
20363         this.fireEvent("load", this, o, o.request.arg);
20364         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20365     },
20366
20367     // private
20368     update : function(dataSet){
20369
20370     },
20371
20372     // private
20373     updateResponse : function(dataSet){
20374
20375     }
20376 });/*
20377  * Based on:
20378  * Ext JS Library 1.1.1
20379  * Copyright(c) 2006-2007, Ext JS, LLC.
20380  *
20381  * Originally Released Under LGPL - original licence link has changed is not relivant.
20382  *
20383  * Fork - LGPL
20384  * <script type="text/javascript">
20385  */
20386
20387 /**
20388  * @class Roo.data.ScriptTagProxy
20389  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20390  * other than the originating domain of the running page.<br><br>
20391  * <p>
20392  * <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
20393  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20394  * <p>
20395  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20396  * source code that is used as the source inside a &lt;script> tag.<br><br>
20397  * <p>
20398  * In order for the browser to process the returned data, the server must wrap the data object
20399  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20400  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20401  * depending on whether the callback name was passed:
20402  * <p>
20403  * <pre><code>
20404 boolean scriptTag = false;
20405 String cb = request.getParameter("callback");
20406 if (cb != null) {
20407     scriptTag = true;
20408     response.setContentType("text/javascript");
20409 } else {
20410     response.setContentType("application/x-json");
20411 }
20412 Writer out = response.getWriter();
20413 if (scriptTag) {
20414     out.write(cb + "(");
20415 }
20416 out.print(dataBlock.toJsonString());
20417 if (scriptTag) {
20418     out.write(");");
20419 }
20420 </pre></code>
20421  *
20422  * @constructor
20423  * @param {Object} config A configuration object.
20424  */
20425 Roo.data.ScriptTagProxy = function(config){
20426     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20427     Roo.apply(this, config);
20428     this.head = document.getElementsByTagName("head")[0];
20429 };
20430
20431 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20432
20433 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20434     /**
20435      * @cfg {String} url The URL from which to request the data object.
20436      */
20437     /**
20438      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20439      */
20440     timeout : 30000,
20441     /**
20442      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20443      * the server the name of the callback function set up by the load call to process the returned data object.
20444      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20445      * javascript output which calls this named function passing the data object as its only parameter.
20446      */
20447     callbackParam : "callback",
20448     /**
20449      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20450      * name to the request.
20451      */
20452     nocache : true,
20453
20454     /**
20455      * Load data from the configured URL, read the data object into
20456      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20457      * process that block using the passed callback.
20458      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20459      * for the request to the remote server.
20460      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20461      * object into a block of Roo.data.Records.
20462      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20463      * The function must be passed <ul>
20464      * <li>The Record block object</li>
20465      * <li>The "arg" argument from the load function</li>
20466      * <li>A boolean success indicator</li>
20467      * </ul>
20468      * @param {Object} scope The scope in which to call the callback
20469      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20470      */
20471     load : function(params, reader, callback, scope, arg){
20472         if(this.fireEvent("beforeload", this, params) !== false){
20473
20474             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20475
20476             var url = this.url;
20477             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20478             if(this.nocache){
20479                 url += "&_dc=" + (new Date().getTime());
20480             }
20481             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20482             var trans = {
20483                 id : transId,
20484                 cb : "stcCallback"+transId,
20485                 scriptId : "stcScript"+transId,
20486                 params : params,
20487                 arg : arg,
20488                 url : url,
20489                 callback : callback,
20490                 scope : scope,
20491                 reader : reader
20492             };
20493             var conn = this;
20494
20495             window[trans.cb] = function(o){
20496                 conn.handleResponse(o, trans);
20497             };
20498
20499             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20500
20501             if(this.autoAbort !== false){
20502                 this.abort();
20503             }
20504
20505             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20506
20507             var script = document.createElement("script");
20508             script.setAttribute("src", url);
20509             script.setAttribute("type", "text/javascript");
20510             script.setAttribute("id", trans.scriptId);
20511             this.head.appendChild(script);
20512
20513             this.trans = trans;
20514         }else{
20515             callback.call(scope||this, null, arg, false);
20516         }
20517     },
20518
20519     // private
20520     isLoading : function(){
20521         return this.trans ? true : false;
20522     },
20523
20524     /**
20525      * Abort the current server request.
20526      */
20527     abort : function(){
20528         if(this.isLoading()){
20529             this.destroyTrans(this.trans);
20530         }
20531     },
20532
20533     // private
20534     destroyTrans : function(trans, isLoaded){
20535         this.head.removeChild(document.getElementById(trans.scriptId));
20536         clearTimeout(trans.timeoutId);
20537         if(isLoaded){
20538             window[trans.cb] = undefined;
20539             try{
20540                 delete window[trans.cb];
20541             }catch(e){}
20542         }else{
20543             // if hasn't been loaded, wait for load to remove it to prevent script error
20544             window[trans.cb] = function(){
20545                 window[trans.cb] = undefined;
20546                 try{
20547                     delete window[trans.cb];
20548                 }catch(e){}
20549             };
20550         }
20551     },
20552
20553     // private
20554     handleResponse : function(o, trans){
20555         this.trans = false;
20556         this.destroyTrans(trans, true);
20557         var result;
20558         try {
20559             result = trans.reader.readRecords(o);
20560         }catch(e){
20561             this.fireEvent("loadexception", this, o, trans.arg, e);
20562             trans.callback.call(trans.scope||window, null, trans.arg, false);
20563             return;
20564         }
20565         this.fireEvent("load", this, o, trans.arg);
20566         trans.callback.call(trans.scope||window, result, trans.arg, true);
20567     },
20568
20569     // private
20570     handleFailure : function(trans){
20571         this.trans = false;
20572         this.destroyTrans(trans, false);
20573         this.fireEvent("loadexception", this, null, trans.arg);
20574         trans.callback.call(trans.scope||window, null, trans.arg, false);
20575     }
20576 });/*
20577  * Based on:
20578  * Ext JS Library 1.1.1
20579  * Copyright(c) 2006-2007, Ext JS, LLC.
20580  *
20581  * Originally Released Under LGPL - original licence link has changed is not relivant.
20582  *
20583  * Fork - LGPL
20584  * <script type="text/javascript">
20585  */
20586
20587 /**
20588  * @class Roo.data.JsonReader
20589  * @extends Roo.data.DataReader
20590  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20591  * based on mappings in a provided Roo.data.Record constructor.
20592  * 
20593  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20594  * in the reply previously. 
20595  * 
20596  * <p>
20597  * Example code:
20598  * <pre><code>
20599 var RecordDef = Roo.data.Record.create([
20600     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20601     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20602 ]);
20603 var myReader = new Roo.data.JsonReader({
20604     totalProperty: "results",    // The property which contains the total dataset size (optional)
20605     root: "rows",                // The property which contains an Array of row objects
20606     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20607 }, RecordDef);
20608 </code></pre>
20609  * <p>
20610  * This would consume a JSON file like this:
20611  * <pre><code>
20612 { 'results': 2, 'rows': [
20613     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20614     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20615 }
20616 </code></pre>
20617  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20618  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20619  * paged from the remote server.
20620  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20621  * @cfg {String} root name of the property which contains the Array of row objects.
20622  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20623  * @constructor
20624  * Create a new JsonReader
20625  * @param {Object} meta Metadata configuration options
20626  * @param {Object} recordType Either an Array of field definition objects,
20627  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20628  */
20629 Roo.data.JsonReader = function(meta, recordType){
20630     
20631     meta = meta || {};
20632     // set some defaults:
20633     Roo.applyIf(meta, {
20634         totalProperty: 'total',
20635         successProperty : 'success',
20636         root : 'data',
20637         id : 'id'
20638     });
20639     
20640     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20641 };
20642 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20643     
20644     /**
20645      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20646      * Used by Store query builder to append _requestMeta to params.
20647      * 
20648      */
20649     metaFromRemote : false,
20650     /**
20651      * This method is only used by a DataProxy which has retrieved data from a remote server.
20652      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20653      * @return {Object} data A data block which is used by an Roo.data.Store object as
20654      * a cache of Roo.data.Records.
20655      */
20656     read : function(response){
20657         var json = response.responseText;
20658        
20659         var o = /* eval:var:o */ eval("("+json+")");
20660         if(!o) {
20661             throw {message: "JsonReader.read: Json object not found"};
20662         }
20663         
20664         if(o.metaData){
20665             
20666             delete this.ef;
20667             this.metaFromRemote = true;
20668             this.meta = o.metaData;
20669             this.recordType = Roo.data.Record.create(o.metaData.fields);
20670             this.onMetaChange(this.meta, this.recordType, o);
20671         }
20672         return this.readRecords(o);
20673     },
20674
20675     // private function a store will implement
20676     onMetaChange : function(meta, recordType, o){
20677
20678     },
20679
20680     /**
20681          * @ignore
20682          */
20683     simpleAccess: function(obj, subsc) {
20684         return obj[subsc];
20685     },
20686
20687         /**
20688          * @ignore
20689          */
20690     getJsonAccessor: function(){
20691         var re = /[\[\.]/;
20692         return function(expr) {
20693             try {
20694                 return(re.test(expr))
20695                     ? new Function("obj", "return obj." + expr)
20696                     : function(obj){
20697                         return obj[expr];
20698                     };
20699             } catch(e){}
20700             return Roo.emptyFn;
20701         };
20702     }(),
20703
20704     /**
20705      * Create a data block containing Roo.data.Records from an XML document.
20706      * @param {Object} o An object which contains an Array of row objects in the property specified
20707      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20708      * which contains the total size of the dataset.
20709      * @return {Object} data A data block which is used by an Roo.data.Store object as
20710      * a cache of Roo.data.Records.
20711      */
20712     readRecords : function(o){
20713         /**
20714          * After any data loads, the raw JSON data is available for further custom processing.
20715          * @type Object
20716          */
20717         this.jsonData = o;
20718         var s = this.meta, Record = this.recordType,
20719             f = Record.prototype.fields, fi = f.items, fl = f.length;
20720
20721 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20722         if (!this.ef) {
20723             if(s.totalProperty) {
20724                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20725                 }
20726                 if(s.successProperty) {
20727                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20728                 }
20729                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20730                 if (s.id) {
20731                         var g = this.getJsonAccessor(s.id);
20732                         this.getId = function(rec) {
20733                                 var r = g(rec);
20734                                 return (r === undefined || r === "") ? null : r;
20735                         };
20736                 } else {
20737                         this.getId = function(){return null;};
20738                 }
20739             this.ef = [];
20740             for(var jj = 0; jj < fl; jj++){
20741                 f = fi[jj];
20742                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20743                 this.ef[jj] = this.getJsonAccessor(map);
20744             }
20745         }
20746
20747         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20748         if(s.totalProperty){
20749             var vt = parseInt(this.getTotal(o), 10);
20750             if(!isNaN(vt)){
20751                 totalRecords = vt;
20752             }
20753         }
20754         if(s.successProperty){
20755             var vs = this.getSuccess(o);
20756             if(vs === false || vs === 'false'){
20757                 success = false;
20758             }
20759         }
20760         var records = [];
20761             for(var i = 0; i < c; i++){
20762                     var n = root[i];
20763                 var values = {};
20764                 var id = this.getId(n);
20765                 for(var j = 0; j < fl; j++){
20766                     f = fi[j];
20767                 var v = this.ef[j](n);
20768                 if (!f.convert) {
20769                     Roo.log('missing convert for ' + f.name);
20770                     Roo.log(f);
20771                     continue;
20772                 }
20773                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20774                 }
20775                 var record = new Record(values, id);
20776                 record.json = n;
20777                 records[i] = record;
20778             }
20779             return {
20780                 success : success,
20781                 records : records,
20782                 totalRecords : totalRecords
20783             };
20784     }
20785 });/*
20786  * Based on:
20787  * Ext JS Library 1.1.1
20788  * Copyright(c) 2006-2007, Ext JS, LLC.
20789  *
20790  * Originally Released Under LGPL - original licence link has changed is not relivant.
20791  *
20792  * Fork - LGPL
20793  * <script type="text/javascript">
20794  */
20795
20796 /**
20797  * @class Roo.data.XmlReader
20798  * @extends Roo.data.DataReader
20799  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20800  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20801  * <p>
20802  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20803  * header in the HTTP response must be set to "text/xml".</em>
20804  * <p>
20805  * Example code:
20806  * <pre><code>
20807 var RecordDef = Roo.data.Record.create([
20808    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20809    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20810 ]);
20811 var myReader = new Roo.data.XmlReader({
20812    totalRecords: "results", // The element which contains the total dataset size (optional)
20813    record: "row",           // The repeated element which contains row information
20814    id: "id"                 // The element within the row that provides an ID for the record (optional)
20815 }, RecordDef);
20816 </code></pre>
20817  * <p>
20818  * This would consume an XML file like this:
20819  * <pre><code>
20820 &lt;?xml?>
20821 &lt;dataset>
20822  &lt;results>2&lt;/results>
20823  &lt;row>
20824    &lt;id>1&lt;/id>
20825    &lt;name>Bill&lt;/name>
20826    &lt;occupation>Gardener&lt;/occupation>
20827  &lt;/row>
20828  &lt;row>
20829    &lt;id>2&lt;/id>
20830    &lt;name>Ben&lt;/name>
20831    &lt;occupation>Horticulturalist&lt;/occupation>
20832  &lt;/row>
20833 &lt;/dataset>
20834 </code></pre>
20835  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20836  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20837  * paged from the remote server.
20838  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20839  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20840  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20841  * a record identifier value.
20842  * @constructor
20843  * Create a new XmlReader
20844  * @param {Object} meta Metadata configuration options
20845  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20846  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20847  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20848  */
20849 Roo.data.XmlReader = function(meta, recordType){
20850     meta = meta || {};
20851     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20852 };
20853 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20854     /**
20855      * This method is only used by a DataProxy which has retrieved data from a remote server.
20856          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20857          * to contain a method called 'responseXML' that returns an XML document object.
20858      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20859      * a cache of Roo.data.Records.
20860      */
20861     read : function(response){
20862         var doc = response.responseXML;
20863         if(!doc) {
20864             throw {message: "XmlReader.read: XML Document not available"};
20865         }
20866         return this.readRecords(doc);
20867     },
20868
20869     /**
20870      * Create a data block containing Roo.data.Records from an XML document.
20871          * @param {Object} doc A parsed XML document.
20872      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20873      * a cache of Roo.data.Records.
20874      */
20875     readRecords : function(doc){
20876         /**
20877          * After any data loads/reads, the raw XML Document is available for further custom processing.
20878          * @type XMLDocument
20879          */
20880         this.xmlData = doc;
20881         var root = doc.documentElement || doc;
20882         var q = Roo.DomQuery;
20883         var recordType = this.recordType, fields = recordType.prototype.fields;
20884         var sid = this.meta.id;
20885         var totalRecords = 0, success = true;
20886         if(this.meta.totalRecords){
20887             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20888         }
20889         
20890         if(this.meta.success){
20891             var sv = q.selectValue(this.meta.success, root, true);
20892             success = sv !== false && sv !== 'false';
20893         }
20894         var records = [];
20895         var ns = q.select(this.meta.record, root);
20896         for(var i = 0, len = ns.length; i < len; i++) {
20897                 var n = ns[i];
20898                 var values = {};
20899                 var id = sid ? q.selectValue(sid, n) : undefined;
20900                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20901                     var f = fields.items[j];
20902                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20903                     v = f.convert(v);
20904                     values[f.name] = v;
20905                 }
20906                 var record = new recordType(values, id);
20907                 record.node = n;
20908                 records[records.length] = record;
20909             }
20910
20911             return {
20912                 success : success,
20913                 records : records,
20914                 totalRecords : totalRecords || records.length
20915             };
20916     }
20917 });/*
20918  * Based on:
20919  * Ext JS Library 1.1.1
20920  * Copyright(c) 2006-2007, Ext JS, LLC.
20921  *
20922  * Originally Released Under LGPL - original licence link has changed is not relivant.
20923  *
20924  * Fork - LGPL
20925  * <script type="text/javascript">
20926  */
20927
20928 /**
20929  * @class Roo.data.ArrayReader
20930  * @extends Roo.data.DataReader
20931  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20932  * Each element of that Array represents a row of data fields. The
20933  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20934  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20935  * <p>
20936  * Example code:.
20937  * <pre><code>
20938 var RecordDef = Roo.data.Record.create([
20939     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20940     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20941 ]);
20942 var myReader = new Roo.data.ArrayReader({
20943     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20944 }, RecordDef);
20945 </code></pre>
20946  * <p>
20947  * This would consume an Array like this:
20948  * <pre><code>
20949 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20950   </code></pre>
20951  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20952  * @constructor
20953  * Create a new JsonReader
20954  * @param {Object} meta Metadata configuration options.
20955  * @param {Object} recordType Either an Array of field definition objects
20956  * as specified to {@link Roo.data.Record#create},
20957  * or an {@link Roo.data.Record} object
20958  * created using {@link Roo.data.Record#create}.
20959  */
20960 Roo.data.ArrayReader = function(meta, recordType){
20961     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20962 };
20963
20964 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20965     /**
20966      * Create a data block containing Roo.data.Records from an XML document.
20967      * @param {Object} o An Array of row objects which represents the dataset.
20968      * @return {Object} data A data block which is used by an Roo.data.Store object as
20969      * a cache of Roo.data.Records.
20970      */
20971     readRecords : function(o){
20972         var sid = this.meta ? this.meta.id : null;
20973         var recordType = this.recordType, fields = recordType.prototype.fields;
20974         var records = [];
20975         var root = o;
20976             for(var i = 0; i < root.length; i++){
20977                     var n = root[i];
20978                 var values = {};
20979                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20980                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20981                 var f = fields.items[j];
20982                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20983                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20984                 v = f.convert(v);
20985                 values[f.name] = v;
20986             }
20987                 var record = new recordType(values, id);
20988                 record.json = n;
20989                 records[records.length] = record;
20990             }
20991             return {
20992                 records : records,
20993                 totalRecords : records.length
20994             };
20995     }
20996 });/*
20997  * Based on:
20998  * Ext JS Library 1.1.1
20999  * Copyright(c) 2006-2007, Ext JS, LLC.
21000  *
21001  * Originally Released Under LGPL - original licence link has changed is not relivant.
21002  *
21003  * Fork - LGPL
21004  * <script type="text/javascript">
21005  */
21006
21007
21008 /**
21009  * @class Roo.data.Tree
21010  * @extends Roo.util.Observable
21011  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21012  * in the tree have most standard DOM functionality.
21013  * @constructor
21014  * @param {Node} root (optional) The root node
21015  */
21016 Roo.data.Tree = function(root){
21017    this.nodeHash = {};
21018    /**
21019     * The root node for this tree
21020     * @type Node
21021     */
21022    this.root = null;
21023    if(root){
21024        this.setRootNode(root);
21025    }
21026    this.addEvents({
21027        /**
21028         * @event append
21029         * Fires when a new child node is appended to a node in this tree.
21030         * @param {Tree} tree The owner tree
21031         * @param {Node} parent The parent node
21032         * @param {Node} node The newly appended node
21033         * @param {Number} index The index of the newly appended node
21034         */
21035        "append" : true,
21036        /**
21037         * @event remove
21038         * Fires when a child node is removed from a node in this tree.
21039         * @param {Tree} tree The owner tree
21040         * @param {Node} parent The parent node
21041         * @param {Node} node The child node removed
21042         */
21043        "remove" : true,
21044        /**
21045         * @event move
21046         * Fires when a node is moved to a new location in the tree
21047         * @param {Tree} tree The owner tree
21048         * @param {Node} node The node moved
21049         * @param {Node} oldParent The old parent of this node
21050         * @param {Node} newParent The new parent of this node
21051         * @param {Number} index The index it was moved to
21052         */
21053        "move" : true,
21054        /**
21055         * @event insert
21056         * Fires when a new child node is inserted in a node in this tree.
21057         * @param {Tree} tree The owner tree
21058         * @param {Node} parent The parent node
21059         * @param {Node} node The child node inserted
21060         * @param {Node} refNode The child node the node was inserted before
21061         */
21062        "insert" : true,
21063        /**
21064         * @event beforeappend
21065         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21066         * @param {Tree} tree The owner tree
21067         * @param {Node} parent The parent node
21068         * @param {Node} node The child node to be appended
21069         */
21070        "beforeappend" : true,
21071        /**
21072         * @event beforeremove
21073         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21074         * @param {Tree} tree The owner tree
21075         * @param {Node} parent The parent node
21076         * @param {Node} node The child node to be removed
21077         */
21078        "beforeremove" : true,
21079        /**
21080         * @event beforemove
21081         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21082         * @param {Tree} tree The owner tree
21083         * @param {Node} node The node being moved
21084         * @param {Node} oldParent The parent of the node
21085         * @param {Node} newParent The new parent the node is moving to
21086         * @param {Number} index The index it is being moved to
21087         */
21088        "beforemove" : true,
21089        /**
21090         * @event beforeinsert
21091         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21092         * @param {Tree} tree The owner tree
21093         * @param {Node} parent The parent node
21094         * @param {Node} node The child node to be inserted
21095         * @param {Node} refNode The child node the node is being inserted before
21096         */
21097        "beforeinsert" : true
21098    });
21099
21100     Roo.data.Tree.superclass.constructor.call(this);
21101 };
21102
21103 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21104     pathSeparator: "/",
21105
21106     proxyNodeEvent : function(){
21107         return this.fireEvent.apply(this, arguments);
21108     },
21109
21110     /**
21111      * Returns the root node for this tree.
21112      * @return {Node}
21113      */
21114     getRootNode : function(){
21115         return this.root;
21116     },
21117
21118     /**
21119      * Sets the root node for this tree.
21120      * @param {Node} node
21121      * @return {Node}
21122      */
21123     setRootNode : function(node){
21124         this.root = node;
21125         node.ownerTree = this;
21126         node.isRoot = true;
21127         this.registerNode(node);
21128         return node;
21129     },
21130
21131     /**
21132      * Gets a node in this tree by its id.
21133      * @param {String} id
21134      * @return {Node}
21135      */
21136     getNodeById : function(id){
21137         return this.nodeHash[id];
21138     },
21139
21140     registerNode : function(node){
21141         this.nodeHash[node.id] = node;
21142     },
21143
21144     unregisterNode : function(node){
21145         delete this.nodeHash[node.id];
21146     },
21147
21148     toString : function(){
21149         return "[Tree"+(this.id?" "+this.id:"")+"]";
21150     }
21151 });
21152
21153 /**
21154  * @class Roo.data.Node
21155  * @extends Roo.util.Observable
21156  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21157  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21158  * @constructor
21159  * @param {Object} attributes The attributes/config for the node
21160  */
21161 Roo.data.Node = function(attributes){
21162     /**
21163      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21164      * @type {Object}
21165      */
21166     this.attributes = attributes || {};
21167     this.leaf = this.attributes.leaf;
21168     /**
21169      * The node id. @type String
21170      */
21171     this.id = this.attributes.id;
21172     if(!this.id){
21173         this.id = Roo.id(null, "ynode-");
21174         this.attributes.id = this.id;
21175     }
21176     /**
21177      * All child nodes of this node. @type Array
21178      */
21179     this.childNodes = [];
21180     if(!this.childNodes.indexOf){ // indexOf is a must
21181         this.childNodes.indexOf = function(o){
21182             for(var i = 0, len = this.length; i < len; i++){
21183                 if(this[i] == o) {
21184                     return i;
21185                 }
21186             }
21187             return -1;
21188         };
21189     }
21190     /**
21191      * The parent node for this node. @type Node
21192      */
21193     this.parentNode = null;
21194     /**
21195      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21196      */
21197     this.firstChild = null;
21198     /**
21199      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21200      */
21201     this.lastChild = null;
21202     /**
21203      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21204      */
21205     this.previousSibling = null;
21206     /**
21207      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21208      */
21209     this.nextSibling = null;
21210
21211     this.addEvents({
21212        /**
21213         * @event append
21214         * Fires when a new child node is appended
21215         * @param {Tree} tree The owner tree
21216         * @param {Node} this This node
21217         * @param {Node} node The newly appended node
21218         * @param {Number} index The index of the newly appended node
21219         */
21220        "append" : true,
21221        /**
21222         * @event remove
21223         * Fires when a child node is removed
21224         * @param {Tree} tree The owner tree
21225         * @param {Node} this This node
21226         * @param {Node} node The removed node
21227         */
21228        "remove" : true,
21229        /**
21230         * @event move
21231         * Fires when this node is moved to a new location in the tree
21232         * @param {Tree} tree The owner tree
21233         * @param {Node} this This node
21234         * @param {Node} oldParent The old parent of this node
21235         * @param {Node} newParent The new parent of this node
21236         * @param {Number} index The index it was moved to
21237         */
21238        "move" : true,
21239        /**
21240         * @event insert
21241         * Fires when a new child node is inserted.
21242         * @param {Tree} tree The owner tree
21243         * @param {Node} this This node
21244         * @param {Node} node The child node inserted
21245         * @param {Node} refNode The child node the node was inserted before
21246         */
21247        "insert" : true,
21248        /**
21249         * @event beforeappend
21250         * Fires before a new child is appended, return false to cancel the append.
21251         * @param {Tree} tree The owner tree
21252         * @param {Node} this This node
21253         * @param {Node} node The child node to be appended
21254         */
21255        "beforeappend" : true,
21256        /**
21257         * @event beforeremove
21258         * Fires before a child is removed, return false to cancel the remove.
21259         * @param {Tree} tree The owner tree
21260         * @param {Node} this This node
21261         * @param {Node} node The child node to be removed
21262         */
21263        "beforeremove" : true,
21264        /**
21265         * @event beforemove
21266         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21267         * @param {Tree} tree The owner tree
21268         * @param {Node} this This node
21269         * @param {Node} oldParent The parent of this node
21270         * @param {Node} newParent The new parent this node is moving to
21271         * @param {Number} index The index it is being moved to
21272         */
21273        "beforemove" : true,
21274        /**
21275         * @event beforeinsert
21276         * Fires before a new child is inserted, return false to cancel the insert.
21277         * @param {Tree} tree The owner tree
21278         * @param {Node} this This node
21279         * @param {Node} node The child node to be inserted
21280         * @param {Node} refNode The child node the node is being inserted before
21281         */
21282        "beforeinsert" : true
21283    });
21284     this.listeners = this.attributes.listeners;
21285     Roo.data.Node.superclass.constructor.call(this);
21286 };
21287
21288 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21289     fireEvent : function(evtName){
21290         // first do standard event for this node
21291         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21292             return false;
21293         }
21294         // then bubble it up to the tree if the event wasn't cancelled
21295         var ot = this.getOwnerTree();
21296         if(ot){
21297             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21298                 return false;
21299             }
21300         }
21301         return true;
21302     },
21303
21304     /**
21305      * Returns true if this node is a leaf
21306      * @return {Boolean}
21307      */
21308     isLeaf : function(){
21309         return this.leaf === true;
21310     },
21311
21312     // private
21313     setFirstChild : function(node){
21314         this.firstChild = node;
21315     },
21316
21317     //private
21318     setLastChild : function(node){
21319         this.lastChild = node;
21320     },
21321
21322
21323     /**
21324      * Returns true if this node is the last child of its parent
21325      * @return {Boolean}
21326      */
21327     isLast : function(){
21328        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21329     },
21330
21331     /**
21332      * Returns true if this node is the first child of its parent
21333      * @return {Boolean}
21334      */
21335     isFirst : function(){
21336        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21337     },
21338
21339     hasChildNodes : function(){
21340         return !this.isLeaf() && this.childNodes.length > 0;
21341     },
21342
21343     /**
21344      * Insert node(s) as the last child node of this node.
21345      * @param {Node/Array} node The node or Array of nodes to append
21346      * @return {Node} The appended node if single append, or null if an array was passed
21347      */
21348     appendChild : function(node){
21349         var multi = false;
21350         if(node instanceof Array){
21351             multi = node;
21352         }else if(arguments.length > 1){
21353             multi = arguments;
21354         }
21355         // if passed an array or multiple args do them one by one
21356         if(multi){
21357             for(var i = 0, len = multi.length; i < len; i++) {
21358                 this.appendChild(multi[i]);
21359             }
21360         }else{
21361             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21362                 return false;
21363             }
21364             var index = this.childNodes.length;
21365             var oldParent = node.parentNode;
21366             // it's a move, make sure we move it cleanly
21367             if(oldParent){
21368                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21369                     return false;
21370                 }
21371                 oldParent.removeChild(node);
21372             }
21373             index = this.childNodes.length;
21374             if(index == 0){
21375                 this.setFirstChild(node);
21376             }
21377             this.childNodes.push(node);
21378             node.parentNode = this;
21379             var ps = this.childNodes[index-1];
21380             if(ps){
21381                 node.previousSibling = ps;
21382                 ps.nextSibling = node;
21383             }else{
21384                 node.previousSibling = null;
21385             }
21386             node.nextSibling = null;
21387             this.setLastChild(node);
21388             node.setOwnerTree(this.getOwnerTree());
21389             this.fireEvent("append", this.ownerTree, this, node, index);
21390             if(oldParent){
21391                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21392             }
21393             return node;
21394         }
21395     },
21396
21397     /**
21398      * Removes a child node from this node.
21399      * @param {Node} node The node to remove
21400      * @return {Node} The removed node
21401      */
21402     removeChild : function(node){
21403         var index = this.childNodes.indexOf(node);
21404         if(index == -1){
21405             return false;
21406         }
21407         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21408             return false;
21409         }
21410
21411         // remove it from childNodes collection
21412         this.childNodes.splice(index, 1);
21413
21414         // update siblings
21415         if(node.previousSibling){
21416             node.previousSibling.nextSibling = node.nextSibling;
21417         }
21418         if(node.nextSibling){
21419             node.nextSibling.previousSibling = node.previousSibling;
21420         }
21421
21422         // update child refs
21423         if(this.firstChild == node){
21424             this.setFirstChild(node.nextSibling);
21425         }
21426         if(this.lastChild == node){
21427             this.setLastChild(node.previousSibling);
21428         }
21429
21430         node.setOwnerTree(null);
21431         // clear any references from the node
21432         node.parentNode = null;
21433         node.previousSibling = null;
21434         node.nextSibling = null;
21435         this.fireEvent("remove", this.ownerTree, this, node);
21436         return node;
21437     },
21438
21439     /**
21440      * Inserts the first node before the second node in this nodes childNodes collection.
21441      * @param {Node} node The node to insert
21442      * @param {Node} refNode The node to insert before (if null the node is appended)
21443      * @return {Node} The inserted node
21444      */
21445     insertBefore : function(node, refNode){
21446         if(!refNode){ // like standard Dom, refNode can be null for append
21447             return this.appendChild(node);
21448         }
21449         // nothing to do
21450         if(node == refNode){
21451             return false;
21452         }
21453
21454         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21455             return false;
21456         }
21457         var index = this.childNodes.indexOf(refNode);
21458         var oldParent = node.parentNode;
21459         var refIndex = index;
21460
21461         // when moving internally, indexes will change after remove
21462         if(oldParent == this && this.childNodes.indexOf(node) < index){
21463             refIndex--;
21464         }
21465
21466         // it's a move, make sure we move it cleanly
21467         if(oldParent){
21468             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21469                 return false;
21470             }
21471             oldParent.removeChild(node);
21472         }
21473         if(refIndex == 0){
21474             this.setFirstChild(node);
21475         }
21476         this.childNodes.splice(refIndex, 0, node);
21477         node.parentNode = this;
21478         var ps = this.childNodes[refIndex-1];
21479         if(ps){
21480             node.previousSibling = ps;
21481             ps.nextSibling = node;
21482         }else{
21483             node.previousSibling = null;
21484         }
21485         node.nextSibling = refNode;
21486         refNode.previousSibling = node;
21487         node.setOwnerTree(this.getOwnerTree());
21488         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21489         if(oldParent){
21490             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21491         }
21492         return node;
21493     },
21494
21495     /**
21496      * Returns the child node at the specified index.
21497      * @param {Number} index
21498      * @return {Node}
21499      */
21500     item : function(index){
21501         return this.childNodes[index];
21502     },
21503
21504     /**
21505      * Replaces one child node in this node with another.
21506      * @param {Node} newChild The replacement node
21507      * @param {Node} oldChild The node to replace
21508      * @return {Node} The replaced node
21509      */
21510     replaceChild : function(newChild, oldChild){
21511         this.insertBefore(newChild, oldChild);
21512         this.removeChild(oldChild);
21513         return oldChild;
21514     },
21515
21516     /**
21517      * Returns the index of a child node
21518      * @param {Node} node
21519      * @return {Number} The index of the node or -1 if it was not found
21520      */
21521     indexOf : function(child){
21522         return this.childNodes.indexOf(child);
21523     },
21524
21525     /**
21526      * Returns the tree this node is in.
21527      * @return {Tree}
21528      */
21529     getOwnerTree : function(){
21530         // if it doesn't have one, look for one
21531         if(!this.ownerTree){
21532             var p = this;
21533             while(p){
21534                 if(p.ownerTree){
21535                     this.ownerTree = p.ownerTree;
21536                     break;
21537                 }
21538                 p = p.parentNode;
21539             }
21540         }
21541         return this.ownerTree;
21542     },
21543
21544     /**
21545      * Returns depth of this node (the root node has a depth of 0)
21546      * @return {Number}
21547      */
21548     getDepth : function(){
21549         var depth = 0;
21550         var p = this;
21551         while(p.parentNode){
21552             ++depth;
21553             p = p.parentNode;
21554         }
21555         return depth;
21556     },
21557
21558     // private
21559     setOwnerTree : function(tree){
21560         // if it's move, we need to update everyone
21561         if(tree != this.ownerTree){
21562             if(this.ownerTree){
21563                 this.ownerTree.unregisterNode(this);
21564             }
21565             this.ownerTree = tree;
21566             var cs = this.childNodes;
21567             for(var i = 0, len = cs.length; i < len; i++) {
21568                 cs[i].setOwnerTree(tree);
21569             }
21570             if(tree){
21571                 tree.registerNode(this);
21572             }
21573         }
21574     },
21575
21576     /**
21577      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21578      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21579      * @return {String} The path
21580      */
21581     getPath : function(attr){
21582         attr = attr || "id";
21583         var p = this.parentNode;
21584         var b = [this.attributes[attr]];
21585         while(p){
21586             b.unshift(p.attributes[attr]);
21587             p = p.parentNode;
21588         }
21589         var sep = this.getOwnerTree().pathSeparator;
21590         return sep + b.join(sep);
21591     },
21592
21593     /**
21594      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21595      * function call will be the scope provided or the current node. The arguments to the function
21596      * will be the args provided or the current node. If the function returns false at any point,
21597      * the bubble is stopped.
21598      * @param {Function} fn The function to call
21599      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21600      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21601      */
21602     bubble : function(fn, scope, args){
21603         var p = this;
21604         while(p){
21605             if(fn.call(scope || p, args || p) === false){
21606                 break;
21607             }
21608             p = p.parentNode;
21609         }
21610     },
21611
21612     /**
21613      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21614      * function call will be the scope provided or the current node. The arguments to the function
21615      * will be the args provided or the current node. If the function returns false at any point,
21616      * the cascade is stopped on that branch.
21617      * @param {Function} fn The function to call
21618      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21619      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21620      */
21621     cascade : function(fn, scope, args){
21622         if(fn.call(scope || this, args || this) !== false){
21623             var cs = this.childNodes;
21624             for(var i = 0, len = cs.length; i < len; i++) {
21625                 cs[i].cascade(fn, scope, args);
21626             }
21627         }
21628     },
21629
21630     /**
21631      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21632      * function call will be the scope provided or the current node. The arguments to the function
21633      * will be the args provided or the current node. If the function returns false at any point,
21634      * the iteration stops.
21635      * @param {Function} fn The function to call
21636      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21637      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21638      */
21639     eachChild : function(fn, scope, args){
21640         var cs = this.childNodes;
21641         for(var i = 0, len = cs.length; i < len; i++) {
21642                 if(fn.call(scope || this, args || cs[i]) === false){
21643                     break;
21644                 }
21645         }
21646     },
21647
21648     /**
21649      * Finds the first child that has the attribute with the specified value.
21650      * @param {String} attribute The attribute name
21651      * @param {Mixed} value The value to search for
21652      * @return {Node} The found child or null if none was found
21653      */
21654     findChild : function(attribute, value){
21655         var cs = this.childNodes;
21656         for(var i = 0, len = cs.length; i < len; i++) {
21657                 if(cs[i].attributes[attribute] == value){
21658                     return cs[i];
21659                 }
21660         }
21661         return null;
21662     },
21663
21664     /**
21665      * Finds the first child by a custom function. The child matches if the function passed
21666      * returns true.
21667      * @param {Function} fn
21668      * @param {Object} scope (optional)
21669      * @return {Node} The found child or null if none was found
21670      */
21671     findChildBy : function(fn, scope){
21672         var cs = this.childNodes;
21673         for(var i = 0, len = cs.length; i < len; i++) {
21674                 if(fn.call(scope||cs[i], cs[i]) === true){
21675                     return cs[i];
21676                 }
21677         }
21678         return null;
21679     },
21680
21681     /**
21682      * Sorts this nodes children using the supplied sort function
21683      * @param {Function} fn
21684      * @param {Object} scope (optional)
21685      */
21686     sort : function(fn, scope){
21687         var cs = this.childNodes;
21688         var len = cs.length;
21689         if(len > 0){
21690             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21691             cs.sort(sortFn);
21692             for(var i = 0; i < len; i++){
21693                 var n = cs[i];
21694                 n.previousSibling = cs[i-1];
21695                 n.nextSibling = cs[i+1];
21696                 if(i == 0){
21697                     this.setFirstChild(n);
21698                 }
21699                 if(i == len-1){
21700                     this.setLastChild(n);
21701                 }
21702             }
21703         }
21704     },
21705
21706     /**
21707      * Returns true if this node is an ancestor (at any point) of the passed node.
21708      * @param {Node} node
21709      * @return {Boolean}
21710      */
21711     contains : function(node){
21712         return node.isAncestor(this);
21713     },
21714
21715     /**
21716      * Returns true if the passed node is an ancestor (at any point) of this node.
21717      * @param {Node} node
21718      * @return {Boolean}
21719      */
21720     isAncestor : function(node){
21721         var p = this.parentNode;
21722         while(p){
21723             if(p == node){
21724                 return true;
21725             }
21726             p = p.parentNode;
21727         }
21728         return false;
21729     },
21730
21731     toString : function(){
21732         return "[Node"+(this.id?" "+this.id:"")+"]";
21733     }
21734 });/*
21735  * Based on:
21736  * Ext JS Library 1.1.1
21737  * Copyright(c) 2006-2007, Ext JS, LLC.
21738  *
21739  * Originally Released Under LGPL - original licence link has changed is not relivant.
21740  *
21741  * Fork - LGPL
21742  * <script type="text/javascript">
21743  */
21744  
21745
21746 /**
21747  * @class Roo.ComponentMgr
21748  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21749  * @singleton
21750  */
21751 Roo.ComponentMgr = function(){
21752     var all = new Roo.util.MixedCollection();
21753
21754     return {
21755         /**
21756          * Registers a component.
21757          * @param {Roo.Component} c The component
21758          */
21759         register : function(c){
21760             all.add(c);
21761         },
21762
21763         /**
21764          * Unregisters a component.
21765          * @param {Roo.Component} c The component
21766          */
21767         unregister : function(c){
21768             all.remove(c);
21769         },
21770
21771         /**
21772          * Returns a component by id
21773          * @param {String} id The component id
21774          */
21775         get : function(id){
21776             return all.get(id);
21777         },
21778
21779         /**
21780          * Registers a function that will be called when a specified component is added to ComponentMgr
21781          * @param {String} id The component id
21782          * @param {Funtction} fn The callback function
21783          * @param {Object} scope The scope of the callback
21784          */
21785         onAvailable : function(id, fn, scope){
21786             all.on("add", function(index, o){
21787                 if(o.id == id){
21788                     fn.call(scope || o, o);
21789                     all.un("add", fn, scope);
21790                 }
21791             });
21792         }
21793     };
21794 }();/*
21795  * Based on:
21796  * Ext JS Library 1.1.1
21797  * Copyright(c) 2006-2007, Ext JS, LLC.
21798  *
21799  * Originally Released Under LGPL - original licence link has changed is not relivant.
21800  *
21801  * Fork - LGPL
21802  * <script type="text/javascript">
21803  */
21804  
21805 /**
21806  * @class Roo.Component
21807  * @extends Roo.util.Observable
21808  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21809  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21810  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21811  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21812  * All visual components (widgets) that require rendering into a layout should subclass Component.
21813  * @constructor
21814  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21815  * 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
21816  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21817  */
21818 Roo.Component = function(config){
21819     config = config || {};
21820     if(config.tagName || config.dom || typeof config == "string"){ // element object
21821         config = {el: config, id: config.id || config};
21822     }
21823     this.initialConfig = config;
21824
21825     Roo.apply(this, config);
21826     this.addEvents({
21827         /**
21828          * @event disable
21829          * Fires after the component is disabled.
21830              * @param {Roo.Component} this
21831              */
21832         disable : true,
21833         /**
21834          * @event enable
21835          * Fires after the component is enabled.
21836              * @param {Roo.Component} this
21837              */
21838         enable : true,
21839         /**
21840          * @event beforeshow
21841          * Fires before the component is shown.  Return false to stop the show.
21842              * @param {Roo.Component} this
21843              */
21844         beforeshow : true,
21845         /**
21846          * @event show
21847          * Fires after the component is shown.
21848              * @param {Roo.Component} this
21849              */
21850         show : true,
21851         /**
21852          * @event beforehide
21853          * Fires before the component is hidden. Return false to stop the hide.
21854              * @param {Roo.Component} this
21855              */
21856         beforehide : true,
21857         /**
21858          * @event hide
21859          * Fires after the component is hidden.
21860              * @param {Roo.Component} this
21861              */
21862         hide : true,
21863         /**
21864          * @event beforerender
21865          * Fires before the component is rendered. Return false to stop the render.
21866              * @param {Roo.Component} this
21867              */
21868         beforerender : true,
21869         /**
21870          * @event render
21871          * Fires after the component is rendered.
21872              * @param {Roo.Component} this
21873              */
21874         render : true,
21875         /**
21876          * @event beforedestroy
21877          * Fires before the component is destroyed. Return false to stop the destroy.
21878              * @param {Roo.Component} this
21879              */
21880         beforedestroy : true,
21881         /**
21882          * @event destroy
21883          * Fires after the component is destroyed.
21884              * @param {Roo.Component} this
21885              */
21886         destroy : true
21887     });
21888     if(!this.id){
21889         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21890     }
21891     Roo.ComponentMgr.register(this);
21892     Roo.Component.superclass.constructor.call(this);
21893     this.initComponent();
21894     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21895         this.render(this.renderTo);
21896         delete this.renderTo;
21897     }
21898 };
21899
21900 /** @private */
21901 Roo.Component.AUTO_ID = 1000;
21902
21903 Roo.extend(Roo.Component, Roo.util.Observable, {
21904     /**
21905      * @scope Roo.Component.prototype
21906      * @type {Boolean}
21907      * true if this component is hidden. Read-only.
21908      */
21909     hidden : false,
21910     /**
21911      * @type {Boolean}
21912      * true if this component is disabled. Read-only.
21913      */
21914     disabled : false,
21915     /**
21916      * @type {Boolean}
21917      * true if this component has been rendered. Read-only.
21918      */
21919     rendered : false,
21920     
21921     /** @cfg {String} disableClass
21922      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21923      */
21924     disabledClass : "x-item-disabled",
21925         /** @cfg {Boolean} allowDomMove
21926          * Whether the component can move the Dom node when rendering (defaults to true).
21927          */
21928     allowDomMove : true,
21929     /** @cfg {String} hideMode
21930      * How this component should hidden. Supported values are
21931      * "visibility" (css visibility), "offsets" (negative offset position) and
21932      * "display" (css display) - defaults to "display".
21933      */
21934     hideMode: 'display',
21935
21936     /** @private */
21937     ctype : "Roo.Component",
21938
21939     /**
21940      * @cfg {String} actionMode 
21941      * which property holds the element that used for  hide() / show() / disable() / enable()
21942      * default is 'el' 
21943      */
21944     actionMode : "el",
21945
21946     /** @private */
21947     getActionEl : function(){
21948         return this[this.actionMode];
21949     },
21950
21951     initComponent : Roo.emptyFn,
21952     /**
21953      * If this is a lazy rendering component, render it to its container element.
21954      * @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.
21955      */
21956     render : function(container, position){
21957         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21958             if(!container && this.el){
21959                 this.el = Roo.get(this.el);
21960                 container = this.el.dom.parentNode;
21961                 this.allowDomMove = false;
21962             }
21963             this.container = Roo.get(container);
21964             this.rendered = true;
21965             if(position !== undefined){
21966                 if(typeof position == 'number'){
21967                     position = this.container.dom.childNodes[position];
21968                 }else{
21969                     position = Roo.getDom(position);
21970                 }
21971             }
21972             this.onRender(this.container, position || null);
21973             if(this.cls){
21974                 this.el.addClass(this.cls);
21975                 delete this.cls;
21976             }
21977             if(this.style){
21978                 this.el.applyStyles(this.style);
21979                 delete this.style;
21980             }
21981             this.fireEvent("render", this);
21982             this.afterRender(this.container);
21983             if(this.hidden){
21984                 this.hide();
21985             }
21986             if(this.disabled){
21987                 this.disable();
21988             }
21989         }
21990         return this;
21991     },
21992
21993     /** @private */
21994     // default function is not really useful
21995     onRender : function(ct, position){
21996         if(this.el){
21997             this.el = Roo.get(this.el);
21998             if(this.allowDomMove !== false){
21999                 ct.dom.insertBefore(this.el.dom, position);
22000             }
22001         }
22002     },
22003
22004     /** @private */
22005     getAutoCreate : function(){
22006         var cfg = typeof this.autoCreate == "object" ?
22007                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22008         if(this.id && !cfg.id){
22009             cfg.id = this.id;
22010         }
22011         return cfg;
22012     },
22013
22014     /** @private */
22015     afterRender : Roo.emptyFn,
22016
22017     /**
22018      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22019      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22020      */
22021     destroy : function(){
22022         if(this.fireEvent("beforedestroy", this) !== false){
22023             this.purgeListeners();
22024             this.beforeDestroy();
22025             if(this.rendered){
22026                 this.el.removeAllListeners();
22027                 this.el.remove();
22028                 if(this.actionMode == "container"){
22029                     this.container.remove();
22030                 }
22031             }
22032             this.onDestroy();
22033             Roo.ComponentMgr.unregister(this);
22034             this.fireEvent("destroy", this);
22035         }
22036     },
22037
22038         /** @private */
22039     beforeDestroy : function(){
22040
22041     },
22042
22043         /** @private */
22044         onDestroy : function(){
22045
22046     },
22047
22048     /**
22049      * Returns the underlying {@link Roo.Element}.
22050      * @return {Roo.Element} The element
22051      */
22052     getEl : function(){
22053         return this.el;
22054     },
22055
22056     /**
22057      * Returns the id of this component.
22058      * @return {String}
22059      */
22060     getId : function(){
22061         return this.id;
22062     },
22063
22064     /**
22065      * Try to focus this component.
22066      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22067      * @return {Roo.Component} this
22068      */
22069     focus : function(selectText){
22070         if(this.rendered){
22071             this.el.focus();
22072             if(selectText === true){
22073                 this.el.dom.select();
22074             }
22075         }
22076         return this;
22077     },
22078
22079     /** @private */
22080     blur : function(){
22081         if(this.rendered){
22082             this.el.blur();
22083         }
22084         return this;
22085     },
22086
22087     /**
22088      * Disable this component.
22089      * @return {Roo.Component} this
22090      */
22091     disable : function(){
22092         if(this.rendered){
22093             this.onDisable();
22094         }
22095         this.disabled = true;
22096         this.fireEvent("disable", this);
22097         return this;
22098     },
22099
22100         // private
22101     onDisable : function(){
22102         this.getActionEl().addClass(this.disabledClass);
22103         this.el.dom.disabled = true;
22104     },
22105
22106     /**
22107      * Enable this component.
22108      * @return {Roo.Component} this
22109      */
22110     enable : function(){
22111         if(this.rendered){
22112             this.onEnable();
22113         }
22114         this.disabled = false;
22115         this.fireEvent("enable", this);
22116         return this;
22117     },
22118
22119         // private
22120     onEnable : function(){
22121         this.getActionEl().removeClass(this.disabledClass);
22122         this.el.dom.disabled = false;
22123     },
22124
22125     /**
22126      * Convenience function for setting disabled/enabled by boolean.
22127      * @param {Boolean} disabled
22128      */
22129     setDisabled : function(disabled){
22130         this[disabled ? "disable" : "enable"]();
22131     },
22132
22133     /**
22134      * Show this component.
22135      * @return {Roo.Component} this
22136      */
22137     show: function(){
22138         if(this.fireEvent("beforeshow", this) !== false){
22139             this.hidden = false;
22140             if(this.rendered){
22141                 this.onShow();
22142             }
22143             this.fireEvent("show", this);
22144         }
22145         return this;
22146     },
22147
22148     // private
22149     onShow : function(){
22150         var ae = this.getActionEl();
22151         if(this.hideMode == 'visibility'){
22152             ae.dom.style.visibility = "visible";
22153         }else if(this.hideMode == 'offsets'){
22154             ae.removeClass('x-hidden');
22155         }else{
22156             ae.dom.style.display = "";
22157         }
22158     },
22159
22160     /**
22161      * Hide this component.
22162      * @return {Roo.Component} this
22163      */
22164     hide: function(){
22165         if(this.fireEvent("beforehide", this) !== false){
22166             this.hidden = true;
22167             if(this.rendered){
22168                 this.onHide();
22169             }
22170             this.fireEvent("hide", this);
22171         }
22172         return this;
22173     },
22174
22175     // private
22176     onHide : function(){
22177         var ae = this.getActionEl();
22178         if(this.hideMode == 'visibility'){
22179             ae.dom.style.visibility = "hidden";
22180         }else if(this.hideMode == 'offsets'){
22181             ae.addClass('x-hidden');
22182         }else{
22183             ae.dom.style.display = "none";
22184         }
22185     },
22186
22187     /**
22188      * Convenience function to hide or show this component by boolean.
22189      * @param {Boolean} visible True to show, false to hide
22190      * @return {Roo.Component} this
22191      */
22192     setVisible: function(visible){
22193         if(visible) {
22194             this.show();
22195         }else{
22196             this.hide();
22197         }
22198         return this;
22199     },
22200
22201     /**
22202      * Returns true if this component is visible.
22203      */
22204     isVisible : function(){
22205         return this.getActionEl().isVisible();
22206     },
22207
22208     cloneConfig : function(overrides){
22209         overrides = overrides || {};
22210         var id = overrides.id || Roo.id();
22211         var cfg = Roo.applyIf(overrides, this.initialConfig);
22212         cfg.id = id; // prevent dup id
22213         return new this.constructor(cfg);
22214     }
22215 });/*
22216  * Based on:
22217  * Ext JS Library 1.1.1
22218  * Copyright(c) 2006-2007, Ext JS, LLC.
22219  *
22220  * Originally Released Under LGPL - original licence link has changed is not relivant.
22221  *
22222  * Fork - LGPL
22223  * <script type="text/javascript">
22224  */
22225  (function(){ 
22226 /**
22227  * @class Roo.Layer
22228  * @extends Roo.Element
22229  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22230  * automatic maintaining of shadow/shim positions.
22231  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22232  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22233  * you can pass a string with a CSS class name. False turns off the shadow.
22234  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22235  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22236  * @cfg {String} cls CSS class to add to the element
22237  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22238  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22239  * @constructor
22240  * @param {Object} config An object with config options.
22241  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22242  */
22243
22244 Roo.Layer = function(config, existingEl){
22245     config = config || {};
22246     var dh = Roo.DomHelper;
22247     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22248     if(existingEl){
22249         this.dom = Roo.getDom(existingEl);
22250     }
22251     if(!this.dom){
22252         var o = config.dh || {tag: "div", cls: "x-layer"};
22253         this.dom = dh.append(pel, o);
22254     }
22255     if(config.cls){
22256         this.addClass(config.cls);
22257     }
22258     this.constrain = config.constrain !== false;
22259     this.visibilityMode = Roo.Element.VISIBILITY;
22260     if(config.id){
22261         this.id = this.dom.id = config.id;
22262     }else{
22263         this.id = Roo.id(this.dom);
22264     }
22265     this.zindex = config.zindex || this.getZIndex();
22266     this.position("absolute", this.zindex);
22267     if(config.shadow){
22268         this.shadowOffset = config.shadowOffset || 4;
22269         this.shadow = new Roo.Shadow({
22270             offset : this.shadowOffset,
22271             mode : config.shadow
22272         });
22273     }else{
22274         this.shadowOffset = 0;
22275     }
22276     this.useShim = config.shim !== false && Roo.useShims;
22277     this.useDisplay = config.useDisplay;
22278     this.hide();
22279 };
22280
22281 var supr = Roo.Element.prototype;
22282
22283 // shims are shared among layer to keep from having 100 iframes
22284 var shims = [];
22285
22286 Roo.extend(Roo.Layer, Roo.Element, {
22287
22288     getZIndex : function(){
22289         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22290     },
22291
22292     getShim : function(){
22293         if(!this.useShim){
22294             return null;
22295         }
22296         if(this.shim){
22297             return this.shim;
22298         }
22299         var shim = shims.shift();
22300         if(!shim){
22301             shim = this.createShim();
22302             shim.enableDisplayMode('block');
22303             shim.dom.style.display = 'none';
22304             shim.dom.style.visibility = 'visible';
22305         }
22306         var pn = this.dom.parentNode;
22307         if(shim.dom.parentNode != pn){
22308             pn.insertBefore(shim.dom, this.dom);
22309         }
22310         shim.setStyle('z-index', this.getZIndex()-2);
22311         this.shim = shim;
22312         return shim;
22313     },
22314
22315     hideShim : function(){
22316         if(this.shim){
22317             this.shim.setDisplayed(false);
22318             shims.push(this.shim);
22319             delete this.shim;
22320         }
22321     },
22322
22323     disableShadow : function(){
22324         if(this.shadow){
22325             this.shadowDisabled = true;
22326             this.shadow.hide();
22327             this.lastShadowOffset = this.shadowOffset;
22328             this.shadowOffset = 0;
22329         }
22330     },
22331
22332     enableShadow : function(show){
22333         if(this.shadow){
22334             this.shadowDisabled = false;
22335             this.shadowOffset = this.lastShadowOffset;
22336             delete this.lastShadowOffset;
22337             if(show){
22338                 this.sync(true);
22339             }
22340         }
22341     },
22342
22343     // private
22344     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22345     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22346     sync : function(doShow){
22347         var sw = this.shadow;
22348         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22349             var sh = this.getShim();
22350
22351             var w = this.getWidth(),
22352                 h = this.getHeight();
22353
22354             var l = this.getLeft(true),
22355                 t = this.getTop(true);
22356
22357             if(sw && !this.shadowDisabled){
22358                 if(doShow && !sw.isVisible()){
22359                     sw.show(this);
22360                 }else{
22361                     sw.realign(l, t, w, h);
22362                 }
22363                 if(sh){
22364                     if(doShow){
22365                        sh.show();
22366                     }
22367                     // fit the shim behind the shadow, so it is shimmed too
22368                     var a = sw.adjusts, s = sh.dom.style;
22369                     s.left = (Math.min(l, l+a.l))+"px";
22370                     s.top = (Math.min(t, t+a.t))+"px";
22371                     s.width = (w+a.w)+"px";
22372                     s.height = (h+a.h)+"px";
22373                 }
22374             }else if(sh){
22375                 if(doShow){
22376                    sh.show();
22377                 }
22378                 sh.setSize(w, h);
22379                 sh.setLeftTop(l, t);
22380             }
22381             
22382         }
22383     },
22384
22385     // private
22386     destroy : function(){
22387         this.hideShim();
22388         if(this.shadow){
22389             this.shadow.hide();
22390         }
22391         this.removeAllListeners();
22392         var pn = this.dom.parentNode;
22393         if(pn){
22394             pn.removeChild(this.dom);
22395         }
22396         Roo.Element.uncache(this.id);
22397     },
22398
22399     remove : function(){
22400         this.destroy();
22401     },
22402
22403     // private
22404     beginUpdate : function(){
22405         this.updating = true;
22406     },
22407
22408     // private
22409     endUpdate : function(){
22410         this.updating = false;
22411         this.sync(true);
22412     },
22413
22414     // private
22415     hideUnders : function(negOffset){
22416         if(this.shadow){
22417             this.shadow.hide();
22418         }
22419         this.hideShim();
22420     },
22421
22422     // private
22423     constrainXY : function(){
22424         if(this.constrain){
22425             var vw = Roo.lib.Dom.getViewWidth(),
22426                 vh = Roo.lib.Dom.getViewHeight();
22427             var s = Roo.get(document).getScroll();
22428
22429             var xy = this.getXY();
22430             var x = xy[0], y = xy[1];   
22431             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22432             // only move it if it needs it
22433             var moved = false;
22434             // first validate right/bottom
22435             if((x + w) > vw+s.left){
22436                 x = vw - w - this.shadowOffset;
22437                 moved = true;
22438             }
22439             if((y + h) > vh+s.top){
22440                 y = vh - h - this.shadowOffset;
22441                 moved = true;
22442             }
22443             // then make sure top/left isn't negative
22444             if(x < s.left){
22445                 x = s.left;
22446                 moved = true;
22447             }
22448             if(y < s.top){
22449                 y = s.top;
22450                 moved = true;
22451             }
22452             if(moved){
22453                 if(this.avoidY){
22454                     var ay = this.avoidY;
22455                     if(y <= ay && (y+h) >= ay){
22456                         y = ay-h-5;   
22457                     }
22458                 }
22459                 xy = [x, y];
22460                 this.storeXY(xy);
22461                 supr.setXY.call(this, xy);
22462                 this.sync();
22463             }
22464         }
22465     },
22466
22467     isVisible : function(){
22468         return this.visible;    
22469     },
22470
22471     // private
22472     showAction : function(){
22473         this.visible = true; // track visibility to prevent getStyle calls
22474         if(this.useDisplay === true){
22475             this.setDisplayed("");
22476         }else if(this.lastXY){
22477             supr.setXY.call(this, this.lastXY);
22478         }else if(this.lastLT){
22479             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22480         }
22481     },
22482
22483     // private
22484     hideAction : function(){
22485         this.visible = false;
22486         if(this.useDisplay === true){
22487             this.setDisplayed(false);
22488         }else{
22489             this.setLeftTop(-10000,-10000);
22490         }
22491     },
22492
22493     // overridden Element method
22494     setVisible : function(v, a, d, c, e){
22495         if(v){
22496             this.showAction();
22497         }
22498         if(a && v){
22499             var cb = function(){
22500                 this.sync(true);
22501                 if(c){
22502                     c();
22503                 }
22504             }.createDelegate(this);
22505             supr.setVisible.call(this, true, true, d, cb, e);
22506         }else{
22507             if(!v){
22508                 this.hideUnders(true);
22509             }
22510             var cb = c;
22511             if(a){
22512                 cb = function(){
22513                     this.hideAction();
22514                     if(c){
22515                         c();
22516                     }
22517                 }.createDelegate(this);
22518             }
22519             supr.setVisible.call(this, v, a, d, cb, e);
22520             if(v){
22521                 this.sync(true);
22522             }else if(!a){
22523                 this.hideAction();
22524             }
22525         }
22526     },
22527
22528     storeXY : function(xy){
22529         delete this.lastLT;
22530         this.lastXY = xy;
22531     },
22532
22533     storeLeftTop : function(left, top){
22534         delete this.lastXY;
22535         this.lastLT = [left, top];
22536     },
22537
22538     // private
22539     beforeFx : function(){
22540         this.beforeAction();
22541         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22542     },
22543
22544     // private
22545     afterFx : function(){
22546         Roo.Layer.superclass.afterFx.apply(this, arguments);
22547         this.sync(this.isVisible());
22548     },
22549
22550     // private
22551     beforeAction : function(){
22552         if(!this.updating && this.shadow){
22553             this.shadow.hide();
22554         }
22555     },
22556
22557     // overridden Element method
22558     setLeft : function(left){
22559         this.storeLeftTop(left, this.getTop(true));
22560         supr.setLeft.apply(this, arguments);
22561         this.sync();
22562     },
22563
22564     setTop : function(top){
22565         this.storeLeftTop(this.getLeft(true), top);
22566         supr.setTop.apply(this, arguments);
22567         this.sync();
22568     },
22569
22570     setLeftTop : function(left, top){
22571         this.storeLeftTop(left, top);
22572         supr.setLeftTop.apply(this, arguments);
22573         this.sync();
22574     },
22575
22576     setXY : function(xy, a, d, c, e){
22577         this.fixDisplay();
22578         this.beforeAction();
22579         this.storeXY(xy);
22580         var cb = this.createCB(c);
22581         supr.setXY.call(this, xy, a, d, cb, e);
22582         if(!a){
22583             cb();
22584         }
22585     },
22586
22587     // private
22588     createCB : function(c){
22589         var el = this;
22590         return function(){
22591             el.constrainXY();
22592             el.sync(true);
22593             if(c){
22594                 c();
22595             }
22596         };
22597     },
22598
22599     // overridden Element method
22600     setX : function(x, a, d, c, e){
22601         this.setXY([x, this.getY()], a, d, c, e);
22602     },
22603
22604     // overridden Element method
22605     setY : function(y, a, d, c, e){
22606         this.setXY([this.getX(), y], a, d, c, e);
22607     },
22608
22609     // overridden Element method
22610     setSize : function(w, h, a, d, c, e){
22611         this.beforeAction();
22612         var cb = this.createCB(c);
22613         supr.setSize.call(this, w, h, a, d, cb, e);
22614         if(!a){
22615             cb();
22616         }
22617     },
22618
22619     // overridden Element method
22620     setWidth : function(w, a, d, c, e){
22621         this.beforeAction();
22622         var cb = this.createCB(c);
22623         supr.setWidth.call(this, w, a, d, cb, e);
22624         if(!a){
22625             cb();
22626         }
22627     },
22628
22629     // overridden Element method
22630     setHeight : function(h, a, d, c, e){
22631         this.beforeAction();
22632         var cb = this.createCB(c);
22633         supr.setHeight.call(this, h, a, d, cb, e);
22634         if(!a){
22635             cb();
22636         }
22637     },
22638
22639     // overridden Element method
22640     setBounds : function(x, y, w, h, a, d, c, e){
22641         this.beforeAction();
22642         var cb = this.createCB(c);
22643         if(!a){
22644             this.storeXY([x, y]);
22645             supr.setXY.call(this, [x, y]);
22646             supr.setSize.call(this, w, h, a, d, cb, e);
22647             cb();
22648         }else{
22649             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22650         }
22651         return this;
22652     },
22653     
22654     /**
22655      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22656      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22657      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22658      * @param {Number} zindex The new z-index to set
22659      * @return {this} The Layer
22660      */
22661     setZIndex : function(zindex){
22662         this.zindex = zindex;
22663         this.setStyle("z-index", zindex + 2);
22664         if(this.shadow){
22665             this.shadow.setZIndex(zindex + 1);
22666         }
22667         if(this.shim){
22668             this.shim.setStyle("z-index", zindex);
22669         }
22670     }
22671 });
22672 })();/*
22673  * Based on:
22674  * Ext JS Library 1.1.1
22675  * Copyright(c) 2006-2007, Ext JS, LLC.
22676  *
22677  * Originally Released Under LGPL - original licence link has changed is not relivant.
22678  *
22679  * Fork - LGPL
22680  * <script type="text/javascript">
22681  */
22682
22683
22684 /**
22685  * @class Roo.Shadow
22686  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22687  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22688  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22689  * @constructor
22690  * Create a new Shadow
22691  * @param {Object} config The config object
22692  */
22693 Roo.Shadow = function(config){
22694     Roo.apply(this, config);
22695     if(typeof this.mode != "string"){
22696         this.mode = this.defaultMode;
22697     }
22698     var o = this.offset, a = {h: 0};
22699     var rad = Math.floor(this.offset/2);
22700     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22701         case "drop":
22702             a.w = 0;
22703             a.l = a.t = o;
22704             a.t -= 1;
22705             if(Roo.isIE){
22706                 a.l -= this.offset + rad;
22707                 a.t -= this.offset + rad;
22708                 a.w -= rad;
22709                 a.h -= rad;
22710                 a.t += 1;
22711             }
22712         break;
22713         case "sides":
22714             a.w = (o*2);
22715             a.l = -o;
22716             a.t = o-1;
22717             if(Roo.isIE){
22718                 a.l -= (this.offset - rad);
22719                 a.t -= this.offset + rad;
22720                 a.l += 1;
22721                 a.w -= (this.offset - rad)*2;
22722                 a.w -= rad + 1;
22723                 a.h -= 1;
22724             }
22725         break;
22726         case "frame":
22727             a.w = a.h = (o*2);
22728             a.l = a.t = -o;
22729             a.t += 1;
22730             a.h -= 2;
22731             if(Roo.isIE){
22732                 a.l -= (this.offset - rad);
22733                 a.t -= (this.offset - rad);
22734                 a.l += 1;
22735                 a.w -= (this.offset + rad + 1);
22736                 a.h -= (this.offset + rad);
22737                 a.h += 1;
22738             }
22739         break;
22740     };
22741
22742     this.adjusts = a;
22743 };
22744
22745 Roo.Shadow.prototype = {
22746     /**
22747      * @cfg {String} mode
22748      * The shadow display mode.  Supports the following options:<br />
22749      * sides: Shadow displays on both sides and bottom only<br />
22750      * frame: Shadow displays equally on all four sides<br />
22751      * drop: Traditional bottom-right drop shadow (default)
22752      */
22753     /**
22754      * @cfg {String} offset
22755      * The number of pixels to offset the shadow from the element (defaults to 4)
22756      */
22757     offset: 4,
22758
22759     // private
22760     defaultMode: "drop",
22761
22762     /**
22763      * Displays the shadow under the target element
22764      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22765      */
22766     show : function(target){
22767         target = Roo.get(target);
22768         if(!this.el){
22769             this.el = Roo.Shadow.Pool.pull();
22770             if(this.el.dom.nextSibling != target.dom){
22771                 this.el.insertBefore(target);
22772             }
22773         }
22774         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22775         if(Roo.isIE){
22776             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22777         }
22778         this.realign(
22779             target.getLeft(true),
22780             target.getTop(true),
22781             target.getWidth(),
22782             target.getHeight()
22783         );
22784         this.el.dom.style.display = "block";
22785     },
22786
22787     /**
22788      * Returns true if the shadow is visible, else false
22789      */
22790     isVisible : function(){
22791         return this.el ? true : false;  
22792     },
22793
22794     /**
22795      * Direct alignment when values are already available. Show must be called at least once before
22796      * calling this method to ensure it is initialized.
22797      * @param {Number} left The target element left position
22798      * @param {Number} top The target element top position
22799      * @param {Number} width The target element width
22800      * @param {Number} height The target element height
22801      */
22802     realign : function(l, t, w, h){
22803         if(!this.el){
22804             return;
22805         }
22806         var a = this.adjusts, d = this.el.dom, s = d.style;
22807         var iea = 0;
22808         s.left = (l+a.l)+"px";
22809         s.top = (t+a.t)+"px";
22810         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22811  
22812         if(s.width != sws || s.height != shs){
22813             s.width = sws;
22814             s.height = shs;
22815             if(!Roo.isIE){
22816                 var cn = d.childNodes;
22817                 var sww = Math.max(0, (sw-12))+"px";
22818                 cn[0].childNodes[1].style.width = sww;
22819                 cn[1].childNodes[1].style.width = sww;
22820                 cn[2].childNodes[1].style.width = sww;
22821                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22822             }
22823         }
22824     },
22825
22826     /**
22827      * Hides this shadow
22828      */
22829     hide : function(){
22830         if(this.el){
22831             this.el.dom.style.display = "none";
22832             Roo.Shadow.Pool.push(this.el);
22833             delete this.el;
22834         }
22835     },
22836
22837     /**
22838      * Adjust the z-index of this shadow
22839      * @param {Number} zindex The new z-index
22840      */
22841     setZIndex : function(z){
22842         this.zIndex = z;
22843         if(this.el){
22844             this.el.setStyle("z-index", z);
22845         }
22846     }
22847 };
22848
22849 // Private utility class that manages the internal Shadow cache
22850 Roo.Shadow.Pool = function(){
22851     var p = [];
22852     var markup = Roo.isIE ?
22853                  '<div class="x-ie-shadow"></div>' :
22854                  '<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>';
22855     return {
22856         pull : function(){
22857             var sh = p.shift();
22858             if(!sh){
22859                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22860                 sh.autoBoxAdjust = false;
22861             }
22862             return sh;
22863         },
22864
22865         push : function(sh){
22866             p.push(sh);
22867         }
22868     };
22869 }();/*
22870  * Based on:
22871  * Ext JS Library 1.1.1
22872  * Copyright(c) 2006-2007, Ext JS, LLC.
22873  *
22874  * Originally Released Under LGPL - original licence link has changed is not relivant.
22875  *
22876  * Fork - LGPL
22877  * <script type="text/javascript">
22878  */
22879
22880 /**
22881  * @class Roo.BoxComponent
22882  * @extends Roo.Component
22883  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22884  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22885  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22886  * layout containers.
22887  * @constructor
22888  * @param {Roo.Element/String/Object} config The configuration options.
22889  */
22890 Roo.BoxComponent = function(config){
22891     Roo.Component.call(this, config);
22892     this.addEvents({
22893         /**
22894          * @event resize
22895          * Fires after the component is resized.
22896              * @param {Roo.Component} this
22897              * @param {Number} adjWidth The box-adjusted width that was set
22898              * @param {Number} adjHeight The box-adjusted height that was set
22899              * @param {Number} rawWidth The width that was originally specified
22900              * @param {Number} rawHeight The height that was originally specified
22901              */
22902         resize : true,
22903         /**
22904          * @event move
22905          * Fires after the component is moved.
22906              * @param {Roo.Component} this
22907              * @param {Number} x The new x position
22908              * @param {Number} y The new y position
22909              */
22910         move : true
22911     });
22912 };
22913
22914 Roo.extend(Roo.BoxComponent, Roo.Component, {
22915     // private, set in afterRender to signify that the component has been rendered
22916     boxReady : false,
22917     // private, used to defer height settings to subclasses
22918     deferHeight: false,
22919     /** @cfg {Number} width
22920      * width (optional) size of component
22921      */
22922      /** @cfg {Number} height
22923      * height (optional) size of component
22924      */
22925      
22926     /**
22927      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22928      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22929      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22930      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22931      * @return {Roo.BoxComponent} this
22932      */
22933     setSize : function(w, h){
22934         // support for standard size objects
22935         if(typeof w == 'object'){
22936             h = w.height;
22937             w = w.width;
22938         }
22939         // not rendered
22940         if(!this.boxReady){
22941             this.width = w;
22942             this.height = h;
22943             return this;
22944         }
22945
22946         // prevent recalcs when not needed
22947         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22948             return this;
22949         }
22950         this.lastSize = {width: w, height: h};
22951
22952         var adj = this.adjustSize(w, h);
22953         var aw = adj.width, ah = adj.height;
22954         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22955             var rz = this.getResizeEl();
22956             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22957                 rz.setSize(aw, ah);
22958             }else if(!this.deferHeight && ah !== undefined){
22959                 rz.setHeight(ah);
22960             }else if(aw !== undefined){
22961                 rz.setWidth(aw);
22962             }
22963             this.onResize(aw, ah, w, h);
22964             this.fireEvent('resize', this, aw, ah, w, h);
22965         }
22966         return this;
22967     },
22968
22969     /**
22970      * Gets the current size of the component's underlying element.
22971      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22972      */
22973     getSize : function(){
22974         return this.el.getSize();
22975     },
22976
22977     /**
22978      * Gets the current XY position of the component's underlying element.
22979      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22980      * @return {Array} The XY position of the element (e.g., [100, 200])
22981      */
22982     getPosition : function(local){
22983         if(local === true){
22984             return [this.el.getLeft(true), this.el.getTop(true)];
22985         }
22986         return this.xy || this.el.getXY();
22987     },
22988
22989     /**
22990      * Gets the current box measurements of the component's underlying element.
22991      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22992      * @returns {Object} box An object in the format {x, y, width, height}
22993      */
22994     getBox : function(local){
22995         var s = this.el.getSize();
22996         if(local){
22997             s.x = this.el.getLeft(true);
22998             s.y = this.el.getTop(true);
22999         }else{
23000             var xy = this.xy || this.el.getXY();
23001             s.x = xy[0];
23002             s.y = xy[1];
23003         }
23004         return s;
23005     },
23006
23007     /**
23008      * Sets the current box measurements of the component's underlying element.
23009      * @param {Object} box An object in the format {x, y, width, height}
23010      * @returns {Roo.BoxComponent} this
23011      */
23012     updateBox : function(box){
23013         this.setSize(box.width, box.height);
23014         this.setPagePosition(box.x, box.y);
23015         return this;
23016     },
23017
23018     // protected
23019     getResizeEl : function(){
23020         return this.resizeEl || this.el;
23021     },
23022
23023     // protected
23024     getPositionEl : function(){
23025         return this.positionEl || this.el;
23026     },
23027
23028     /**
23029      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23030      * This method fires the move event.
23031      * @param {Number} left The new left
23032      * @param {Number} top The new top
23033      * @returns {Roo.BoxComponent} this
23034      */
23035     setPosition : function(x, y){
23036         this.x = x;
23037         this.y = y;
23038         if(!this.boxReady){
23039             return this;
23040         }
23041         var adj = this.adjustPosition(x, y);
23042         var ax = adj.x, ay = adj.y;
23043
23044         var el = this.getPositionEl();
23045         if(ax !== undefined || ay !== undefined){
23046             if(ax !== undefined && ay !== undefined){
23047                 el.setLeftTop(ax, ay);
23048             }else if(ax !== undefined){
23049                 el.setLeft(ax);
23050             }else if(ay !== undefined){
23051                 el.setTop(ay);
23052             }
23053             this.onPosition(ax, ay);
23054             this.fireEvent('move', this, ax, ay);
23055         }
23056         return this;
23057     },
23058
23059     /**
23060      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23061      * This method fires the move event.
23062      * @param {Number} x The new x position
23063      * @param {Number} y The new y position
23064      * @returns {Roo.BoxComponent} this
23065      */
23066     setPagePosition : function(x, y){
23067         this.pageX = x;
23068         this.pageY = y;
23069         if(!this.boxReady){
23070             return;
23071         }
23072         if(x === undefined || y === undefined){ // cannot translate undefined points
23073             return;
23074         }
23075         var p = this.el.translatePoints(x, y);
23076         this.setPosition(p.left, p.top);
23077         return this;
23078     },
23079
23080     // private
23081     onRender : function(ct, position){
23082         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23083         if(this.resizeEl){
23084             this.resizeEl = Roo.get(this.resizeEl);
23085         }
23086         if(this.positionEl){
23087             this.positionEl = Roo.get(this.positionEl);
23088         }
23089     },
23090
23091     // private
23092     afterRender : function(){
23093         Roo.BoxComponent.superclass.afterRender.call(this);
23094         this.boxReady = true;
23095         this.setSize(this.width, this.height);
23096         if(this.x || this.y){
23097             this.setPosition(this.x, this.y);
23098         }
23099         if(this.pageX || this.pageY){
23100             this.setPagePosition(this.pageX, this.pageY);
23101         }
23102     },
23103
23104     /**
23105      * Force the component's size to recalculate based on the underlying element's current height and width.
23106      * @returns {Roo.BoxComponent} this
23107      */
23108     syncSize : function(){
23109         delete this.lastSize;
23110         this.setSize(this.el.getWidth(), this.el.getHeight());
23111         return this;
23112     },
23113
23114     /**
23115      * Called after the component is resized, this method is empty by default but can be implemented by any
23116      * subclass that needs to perform custom logic after a resize occurs.
23117      * @param {Number} adjWidth The box-adjusted width that was set
23118      * @param {Number} adjHeight The box-adjusted height that was set
23119      * @param {Number} rawWidth The width that was originally specified
23120      * @param {Number} rawHeight The height that was originally specified
23121      */
23122     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23123
23124     },
23125
23126     /**
23127      * Called after the component is moved, this method is empty by default but can be implemented by any
23128      * subclass that needs to perform custom logic after a move occurs.
23129      * @param {Number} x The new x position
23130      * @param {Number} y The new y position
23131      */
23132     onPosition : function(x, y){
23133
23134     },
23135
23136     // private
23137     adjustSize : function(w, h){
23138         if(this.autoWidth){
23139             w = 'auto';
23140         }
23141         if(this.autoHeight){
23142             h = 'auto';
23143         }
23144         return {width : w, height: h};
23145     },
23146
23147     // private
23148     adjustPosition : function(x, y){
23149         return {x : x, y: y};
23150     }
23151 });/*
23152  * Based on:
23153  * Ext JS Library 1.1.1
23154  * Copyright(c) 2006-2007, Ext JS, LLC.
23155  *
23156  * Originally Released Under LGPL - original licence link has changed is not relivant.
23157  *
23158  * Fork - LGPL
23159  * <script type="text/javascript">
23160  */
23161
23162
23163 /**
23164  * @class Roo.SplitBar
23165  * @extends Roo.util.Observable
23166  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23167  * <br><br>
23168  * Usage:
23169  * <pre><code>
23170 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23171                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23172 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23173 split.minSize = 100;
23174 split.maxSize = 600;
23175 split.animate = true;
23176 split.on('moved', splitterMoved);
23177 </code></pre>
23178  * @constructor
23179  * Create a new SplitBar
23180  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23181  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23182  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23183  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23184                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23185                         position of the SplitBar).
23186  */
23187 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23188     
23189     /** @private */
23190     this.el = Roo.get(dragElement, true);
23191     this.el.dom.unselectable = "on";
23192     /** @private */
23193     this.resizingEl = Roo.get(resizingElement, true);
23194
23195     /**
23196      * @private
23197      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23198      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23199      * @type Number
23200      */
23201     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23202     
23203     /**
23204      * The minimum size of the resizing element. (Defaults to 0)
23205      * @type Number
23206      */
23207     this.minSize = 0;
23208     
23209     /**
23210      * The maximum size of the resizing element. (Defaults to 2000)
23211      * @type Number
23212      */
23213     this.maxSize = 2000;
23214     
23215     /**
23216      * Whether to animate the transition to the new size
23217      * @type Boolean
23218      */
23219     this.animate = false;
23220     
23221     /**
23222      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23223      * @type Boolean
23224      */
23225     this.useShim = false;
23226     
23227     /** @private */
23228     this.shim = null;
23229     
23230     if(!existingProxy){
23231         /** @private */
23232         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23233     }else{
23234         this.proxy = Roo.get(existingProxy).dom;
23235     }
23236     /** @private */
23237     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23238     
23239     /** @private */
23240     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23241     
23242     /** @private */
23243     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23244     
23245     /** @private */
23246     this.dragSpecs = {};
23247     
23248     /**
23249      * @private The adapter to use to positon and resize elements
23250      */
23251     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23252     this.adapter.init(this);
23253     
23254     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23255         /** @private */
23256         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23257         this.el.addClass("x-splitbar-h");
23258     }else{
23259         /** @private */
23260         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23261         this.el.addClass("x-splitbar-v");
23262     }
23263     
23264     this.addEvents({
23265         /**
23266          * @event resize
23267          * Fires when the splitter is moved (alias for {@link #event-moved})
23268          * @param {Roo.SplitBar} this
23269          * @param {Number} newSize the new width or height
23270          */
23271         "resize" : true,
23272         /**
23273          * @event moved
23274          * Fires when the splitter is moved
23275          * @param {Roo.SplitBar} this
23276          * @param {Number} newSize the new width or height
23277          */
23278         "moved" : true,
23279         /**
23280          * @event beforeresize
23281          * Fires before the splitter is dragged
23282          * @param {Roo.SplitBar} this
23283          */
23284         "beforeresize" : true,
23285
23286         "beforeapply" : true
23287     });
23288
23289     Roo.util.Observable.call(this);
23290 };
23291
23292 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23293     onStartProxyDrag : function(x, y){
23294         this.fireEvent("beforeresize", this);
23295         if(!this.overlay){
23296             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23297             o.unselectable();
23298             o.enableDisplayMode("block");
23299             // all splitbars share the same overlay
23300             Roo.SplitBar.prototype.overlay = o;
23301         }
23302         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23303         this.overlay.show();
23304         Roo.get(this.proxy).setDisplayed("block");
23305         var size = this.adapter.getElementSize(this);
23306         this.activeMinSize = this.getMinimumSize();;
23307         this.activeMaxSize = this.getMaximumSize();;
23308         var c1 = size - this.activeMinSize;
23309         var c2 = Math.max(this.activeMaxSize - size, 0);
23310         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23311             this.dd.resetConstraints();
23312             this.dd.setXConstraint(
23313                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23314                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23315             );
23316             this.dd.setYConstraint(0, 0);
23317         }else{
23318             this.dd.resetConstraints();
23319             this.dd.setXConstraint(0, 0);
23320             this.dd.setYConstraint(
23321                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23322                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23323             );
23324          }
23325         this.dragSpecs.startSize = size;
23326         this.dragSpecs.startPoint = [x, y];
23327         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23328     },
23329     
23330     /** 
23331      * @private Called after the drag operation by the DDProxy
23332      */
23333     onEndProxyDrag : function(e){
23334         Roo.get(this.proxy).setDisplayed(false);
23335         var endPoint = Roo.lib.Event.getXY(e);
23336         if(this.overlay){
23337             this.overlay.hide();
23338         }
23339         var newSize;
23340         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23341             newSize = this.dragSpecs.startSize + 
23342                 (this.placement == Roo.SplitBar.LEFT ?
23343                     endPoint[0] - this.dragSpecs.startPoint[0] :
23344                     this.dragSpecs.startPoint[0] - endPoint[0]
23345                 );
23346         }else{
23347             newSize = this.dragSpecs.startSize + 
23348                 (this.placement == Roo.SplitBar.TOP ?
23349                     endPoint[1] - this.dragSpecs.startPoint[1] :
23350                     this.dragSpecs.startPoint[1] - endPoint[1]
23351                 );
23352         }
23353         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23354         if(newSize != this.dragSpecs.startSize){
23355             if(this.fireEvent('beforeapply', this, newSize) !== false){
23356                 this.adapter.setElementSize(this, newSize);
23357                 this.fireEvent("moved", this, newSize);
23358                 this.fireEvent("resize", this, newSize);
23359             }
23360         }
23361     },
23362     
23363     /**
23364      * Get the adapter this SplitBar uses
23365      * @return The adapter object
23366      */
23367     getAdapter : function(){
23368         return this.adapter;
23369     },
23370     
23371     /**
23372      * Set the adapter this SplitBar uses
23373      * @param {Object} adapter A SplitBar adapter object
23374      */
23375     setAdapter : function(adapter){
23376         this.adapter = adapter;
23377         this.adapter.init(this);
23378     },
23379     
23380     /**
23381      * Gets the minimum size for the resizing element
23382      * @return {Number} The minimum size
23383      */
23384     getMinimumSize : function(){
23385         return this.minSize;
23386     },
23387     
23388     /**
23389      * Sets the minimum size for the resizing element
23390      * @param {Number} minSize The minimum size
23391      */
23392     setMinimumSize : function(minSize){
23393         this.minSize = minSize;
23394     },
23395     
23396     /**
23397      * Gets the maximum size for the resizing element
23398      * @return {Number} The maximum size
23399      */
23400     getMaximumSize : function(){
23401         return this.maxSize;
23402     },
23403     
23404     /**
23405      * Sets the maximum size for the resizing element
23406      * @param {Number} maxSize The maximum size
23407      */
23408     setMaximumSize : function(maxSize){
23409         this.maxSize = maxSize;
23410     },
23411     
23412     /**
23413      * Sets the initialize size for the resizing element
23414      * @param {Number} size The initial size
23415      */
23416     setCurrentSize : function(size){
23417         var oldAnimate = this.animate;
23418         this.animate = false;
23419         this.adapter.setElementSize(this, size);
23420         this.animate = oldAnimate;
23421     },
23422     
23423     /**
23424      * Destroy this splitbar. 
23425      * @param {Boolean} removeEl True to remove the element
23426      */
23427     destroy : function(removeEl){
23428         if(this.shim){
23429             this.shim.remove();
23430         }
23431         this.dd.unreg();
23432         this.proxy.parentNode.removeChild(this.proxy);
23433         if(removeEl){
23434             this.el.remove();
23435         }
23436     }
23437 });
23438
23439 /**
23440  * @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.
23441  */
23442 Roo.SplitBar.createProxy = function(dir){
23443     var proxy = new Roo.Element(document.createElement("div"));
23444     proxy.unselectable();
23445     var cls = 'x-splitbar-proxy';
23446     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23447     document.body.appendChild(proxy.dom);
23448     return proxy.dom;
23449 };
23450
23451 /** 
23452  * @class Roo.SplitBar.BasicLayoutAdapter
23453  * Default Adapter. It assumes the splitter and resizing element are not positioned
23454  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23455  */
23456 Roo.SplitBar.BasicLayoutAdapter = function(){
23457 };
23458
23459 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23460     // do nothing for now
23461     init : function(s){
23462     
23463     },
23464     /**
23465      * Called before drag operations to get the current size of the resizing element. 
23466      * @param {Roo.SplitBar} s The SplitBar using this adapter
23467      */
23468      getElementSize : function(s){
23469         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23470             return s.resizingEl.getWidth();
23471         }else{
23472             return s.resizingEl.getHeight();
23473         }
23474     },
23475     
23476     /**
23477      * Called after drag operations to set the size of the resizing element.
23478      * @param {Roo.SplitBar} s The SplitBar using this adapter
23479      * @param {Number} newSize The new size to set
23480      * @param {Function} onComplete A function to be invoked when resizing is complete
23481      */
23482     setElementSize : function(s, newSize, onComplete){
23483         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23484             if(!s.animate){
23485                 s.resizingEl.setWidth(newSize);
23486                 if(onComplete){
23487                     onComplete(s, newSize);
23488                 }
23489             }else{
23490                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23491             }
23492         }else{
23493             
23494             if(!s.animate){
23495                 s.resizingEl.setHeight(newSize);
23496                 if(onComplete){
23497                     onComplete(s, newSize);
23498                 }
23499             }else{
23500                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23501             }
23502         }
23503     }
23504 };
23505
23506 /** 
23507  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23508  * @extends Roo.SplitBar.BasicLayoutAdapter
23509  * Adapter that  moves the splitter element to align with the resized sizing element. 
23510  * Used with an absolute positioned SplitBar.
23511  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23512  * document.body, make sure you assign an id to the body element.
23513  */
23514 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23515     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23516     this.container = Roo.get(container);
23517 };
23518
23519 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23520     init : function(s){
23521         this.basic.init(s);
23522     },
23523     
23524     getElementSize : function(s){
23525         return this.basic.getElementSize(s);
23526     },
23527     
23528     setElementSize : function(s, newSize, onComplete){
23529         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23530     },
23531     
23532     moveSplitter : function(s){
23533         var yes = Roo.SplitBar;
23534         switch(s.placement){
23535             case yes.LEFT:
23536                 s.el.setX(s.resizingEl.getRight());
23537                 break;
23538             case yes.RIGHT:
23539                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23540                 break;
23541             case yes.TOP:
23542                 s.el.setY(s.resizingEl.getBottom());
23543                 break;
23544             case yes.BOTTOM:
23545                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23546                 break;
23547         }
23548     }
23549 };
23550
23551 /**
23552  * Orientation constant - Create a vertical SplitBar
23553  * @static
23554  * @type Number
23555  */
23556 Roo.SplitBar.VERTICAL = 1;
23557
23558 /**
23559  * Orientation constant - Create a horizontal SplitBar
23560  * @static
23561  * @type Number
23562  */
23563 Roo.SplitBar.HORIZONTAL = 2;
23564
23565 /**
23566  * Placement constant - The resizing element is to the left of the splitter element
23567  * @static
23568  * @type Number
23569  */
23570 Roo.SplitBar.LEFT = 1;
23571
23572 /**
23573  * Placement constant - The resizing element is to the right of the splitter element
23574  * @static
23575  * @type Number
23576  */
23577 Roo.SplitBar.RIGHT = 2;
23578
23579 /**
23580  * Placement constant - The resizing element is positioned above the splitter element
23581  * @static
23582  * @type Number
23583  */
23584 Roo.SplitBar.TOP = 3;
23585
23586 /**
23587  * Placement constant - The resizing element is positioned under splitter element
23588  * @static
23589  * @type Number
23590  */
23591 Roo.SplitBar.BOTTOM = 4;
23592 /*
23593  * Based on:
23594  * Ext JS Library 1.1.1
23595  * Copyright(c) 2006-2007, Ext JS, LLC.
23596  *
23597  * Originally Released Under LGPL - original licence link has changed is not relivant.
23598  *
23599  * Fork - LGPL
23600  * <script type="text/javascript">
23601  */
23602
23603 /**
23604  * @class Roo.View
23605  * @extends Roo.util.Observable
23606  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23607  * This class also supports single and multi selection modes. <br>
23608  * Create a data model bound view:
23609  <pre><code>
23610  var store = new Roo.data.Store(...);
23611
23612  var view = new Roo.View({
23613     el : "my-element",
23614     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23615  
23616     singleSelect: true,
23617     selectedClass: "ydataview-selected",
23618     store: store
23619  });
23620
23621  // listen for node click?
23622  view.on("click", function(vw, index, node, e){
23623  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23624  });
23625
23626  // load XML data
23627  dataModel.load("foobar.xml");
23628  </code></pre>
23629  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23630  * <br><br>
23631  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23632  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23633  * 
23634  * Note: old style constructor is still suported (container, template, config)
23635  * 
23636  * @constructor
23637  * Create a new View
23638  * @param {Object} config The config object
23639  * 
23640  */
23641 Roo.View = function(config, depreciated_tpl, depreciated_config){
23642     
23643     if (typeof(depreciated_tpl) == 'undefined') {
23644         // new way.. - universal constructor.
23645         Roo.apply(this, config);
23646         this.el  = Roo.get(this.el);
23647     } else {
23648         // old format..
23649         this.el  = Roo.get(config);
23650         this.tpl = depreciated_tpl;
23651         Roo.apply(this, depreciated_config);
23652     }
23653      
23654     
23655     if(typeof(this.tpl) == "string"){
23656         this.tpl = new Roo.Template(this.tpl);
23657     } else {
23658         // support xtype ctors..
23659         this.tpl = new Roo.factory(this.tpl, Roo);
23660     }
23661     
23662     
23663     this.tpl.compile();
23664    
23665
23666      
23667     /** @private */
23668     this.addEvents({
23669         /**
23670          * @event beforeclick
23671          * Fires before a click is processed. Returns false to cancel the default action.
23672          * @param {Roo.View} this
23673          * @param {Number} index The index of the target node
23674          * @param {HTMLElement} node The target node
23675          * @param {Roo.EventObject} e The raw event object
23676          */
23677             "beforeclick" : true,
23678         /**
23679          * @event click
23680          * Fires when a template node is clicked.
23681          * @param {Roo.View} this
23682          * @param {Number} index The index of the target node
23683          * @param {HTMLElement} node The target node
23684          * @param {Roo.EventObject} e The raw event object
23685          */
23686             "click" : true,
23687         /**
23688          * @event dblclick
23689          * Fires when a template node is double clicked.
23690          * @param {Roo.View} this
23691          * @param {Number} index The index of the target node
23692          * @param {HTMLElement} node The target node
23693          * @param {Roo.EventObject} e The raw event object
23694          */
23695             "dblclick" : true,
23696         /**
23697          * @event contextmenu
23698          * Fires when a template node is right clicked.
23699          * @param {Roo.View} this
23700          * @param {Number} index The index of the target node
23701          * @param {HTMLElement} node The target node
23702          * @param {Roo.EventObject} e The raw event object
23703          */
23704             "contextmenu" : true,
23705         /**
23706          * @event selectionchange
23707          * Fires when the selected nodes change.
23708          * @param {Roo.View} this
23709          * @param {Array} selections Array of the selected nodes
23710          */
23711             "selectionchange" : true,
23712     
23713         /**
23714          * @event beforeselect
23715          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23716          * @param {Roo.View} this
23717          * @param {HTMLElement} node The node to be selected
23718          * @param {Array} selections Array of currently selected nodes
23719          */
23720             "beforeselect" : true,
23721         /**
23722          * @event preparedata
23723          * Fires on every row to render, to allow you to change the data.
23724          * @param {Roo.View} this
23725          * @param {Object} data to be rendered (change this)
23726          */
23727           "preparedata" : true
23728         });
23729
23730     this.el.on({
23731         "click": this.onClick,
23732         "dblclick": this.onDblClick,
23733         "contextmenu": this.onContextMenu,
23734         scope:this
23735     });
23736
23737     this.selections = [];
23738     this.nodes = [];
23739     this.cmp = new Roo.CompositeElementLite([]);
23740     if(this.store){
23741         this.store = Roo.factory(this.store, Roo.data);
23742         this.setStore(this.store, true);
23743     }
23744     Roo.View.superclass.constructor.call(this);
23745 };
23746
23747 Roo.extend(Roo.View, Roo.util.Observable, {
23748     
23749      /**
23750      * @cfg {Roo.data.Store} store Data store to load data from.
23751      */
23752     store : false,
23753     
23754     /**
23755      * @cfg {String|Roo.Element} el The container element.
23756      */
23757     el : '',
23758     
23759     /**
23760      * @cfg {String|Roo.Template} tpl The template used by this View 
23761      */
23762     tpl : false,
23763     
23764     /**
23765      * @cfg {String} selectedClass The css class to add to selected nodes
23766      */
23767     selectedClass : "x-view-selected",
23768      /**
23769      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23770      */
23771     emptyText : "",
23772     /**
23773      * @cfg {Boolean} multiSelect Allow multiple selection
23774      */
23775     multiSelect : false,
23776     /**
23777      * @cfg {Boolean} singleSelect Allow single selection
23778      */
23779     singleSelect:  false,
23780     
23781     /**
23782      * @cfg {Boolean} toggleSelect - selecting 
23783      */
23784     toggleSelect : false,
23785     
23786     /**
23787      * Returns the element this view is bound to.
23788      * @return {Roo.Element}
23789      */
23790     getEl : function(){
23791         return this.el;
23792     },
23793
23794     /**
23795      * Refreshes the view.
23796      */
23797     refresh : function(){
23798         var t = this.tpl;
23799         this.clearSelections();
23800         this.el.update("");
23801         var html = [];
23802         var records = this.store.getRange();
23803         if(records.length < 1){
23804             this.el.update(this.emptyText);
23805             return;
23806         }
23807         for(var i = 0, len = records.length; i < len; i++){
23808             var data = this.prepareData(records[i].data, i, records[i]);
23809             this.fireEvent("preparedata", this, data, i, records[i]);
23810             html[html.length] = t.apply(data);
23811         }
23812         this.el.update(html.join(""));
23813         this.nodes = this.el.dom.childNodes;
23814         this.updateIndexes(0);
23815     },
23816
23817     /**
23818      * Function to override to reformat the data that is sent to
23819      * the template for each node.
23820      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23821      * a JSON object for an UpdateManager bound view).
23822      */
23823     prepareData : function(data){
23824         return data;
23825     },
23826
23827     onUpdate : function(ds, record){
23828         this.clearSelections();
23829         var index = this.store.indexOf(record);
23830         var n = this.nodes[index];
23831         this.tpl.insertBefore(n, this.prepareData(record.data));
23832         n.parentNode.removeChild(n);
23833         this.updateIndexes(index, index);
23834     },
23835
23836     onAdd : function(ds, records, index){
23837         this.clearSelections();
23838         if(this.nodes.length == 0){
23839             this.refresh();
23840             return;
23841         }
23842         var n = this.nodes[index];
23843         for(var i = 0, len = records.length; i < len; i++){
23844             var d = this.prepareData(records[i].data);
23845             if(n){
23846                 this.tpl.insertBefore(n, d);
23847             }else{
23848                 this.tpl.append(this.el, d);
23849             }
23850         }
23851         this.updateIndexes(index);
23852     },
23853
23854     onRemove : function(ds, record, index){
23855         this.clearSelections();
23856         this.el.dom.removeChild(this.nodes[index]);
23857         this.updateIndexes(index);
23858     },
23859
23860     /**
23861      * Refresh an individual node.
23862      * @param {Number} index
23863      */
23864     refreshNode : function(index){
23865         this.onUpdate(this.store, this.store.getAt(index));
23866     },
23867
23868     updateIndexes : function(startIndex, endIndex){
23869         var ns = this.nodes;
23870         startIndex = startIndex || 0;
23871         endIndex = endIndex || ns.length - 1;
23872         for(var i = startIndex; i <= endIndex; i++){
23873             ns[i].nodeIndex = i;
23874         }
23875     },
23876
23877     /**
23878      * Changes the data store this view uses and refresh the view.
23879      * @param {Store} store
23880      */
23881     setStore : function(store, initial){
23882         if(!initial && this.store){
23883             this.store.un("datachanged", this.refresh);
23884             this.store.un("add", this.onAdd);
23885             this.store.un("remove", this.onRemove);
23886             this.store.un("update", this.onUpdate);
23887             this.store.un("clear", this.refresh);
23888         }
23889         if(store){
23890           
23891             store.on("datachanged", this.refresh, this);
23892             store.on("add", this.onAdd, this);
23893             store.on("remove", this.onRemove, this);
23894             store.on("update", this.onUpdate, this);
23895             store.on("clear", this.refresh, this);
23896         }
23897         
23898         if(store){
23899             this.refresh();
23900         }
23901     },
23902
23903     /**
23904      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23905      * @param {HTMLElement} node
23906      * @return {HTMLElement} The template node
23907      */
23908     findItemFromChild : function(node){
23909         var el = this.el.dom;
23910         if(!node || node.parentNode == el){
23911                     return node;
23912             }
23913             var p = node.parentNode;
23914             while(p && p != el){
23915             if(p.parentNode == el){
23916                 return p;
23917             }
23918             p = p.parentNode;
23919         }
23920             return null;
23921     },
23922
23923     /** @ignore */
23924     onClick : function(e){
23925         var item = this.findItemFromChild(e.getTarget());
23926         if(item){
23927             var index = this.indexOf(item);
23928             if(this.onItemClick(item, index, e) !== false){
23929                 this.fireEvent("click", this, index, item, e);
23930             }
23931         }else{
23932             this.clearSelections();
23933         }
23934     },
23935
23936     /** @ignore */
23937     onContextMenu : function(e){
23938         var item = this.findItemFromChild(e.getTarget());
23939         if(item){
23940             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23941         }
23942     },
23943
23944     /** @ignore */
23945     onDblClick : function(e){
23946         var item = this.findItemFromChild(e.getTarget());
23947         if(item){
23948             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23949         }
23950     },
23951
23952     onItemClick : function(item, index, e)
23953     {
23954         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23955             return false;
23956         }
23957         if (this.toggleSelect) {
23958             var m = this.isSelected(item) ? 'unselect' : 'select';
23959             Roo.log(m);
23960             var _t = this;
23961             _t[m](item, true, false);
23962             return true;
23963         }
23964         if(this.multiSelect || this.singleSelect){
23965             if(this.multiSelect && e.shiftKey && this.lastSelection){
23966                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23967             }else{
23968                 this.select(item, this.multiSelect && e.ctrlKey);
23969                 this.lastSelection = item;
23970             }
23971             e.preventDefault();
23972         }
23973         return true;
23974     },
23975
23976     /**
23977      * Get the number of selected nodes.
23978      * @return {Number}
23979      */
23980     getSelectionCount : function(){
23981         return this.selections.length;
23982     },
23983
23984     /**
23985      * Get the currently selected nodes.
23986      * @return {Array} An array of HTMLElements
23987      */
23988     getSelectedNodes : function(){
23989         return this.selections;
23990     },
23991
23992     /**
23993      * Get the indexes of the selected nodes.
23994      * @return {Array}
23995      */
23996     getSelectedIndexes : function(){
23997         var indexes = [], s = this.selections;
23998         for(var i = 0, len = s.length; i < len; i++){
23999             indexes.push(s[i].nodeIndex);
24000         }
24001         return indexes;
24002     },
24003
24004     /**
24005      * Clear all selections
24006      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24007      */
24008     clearSelections : function(suppressEvent){
24009         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24010             this.cmp.elements = this.selections;
24011             this.cmp.removeClass(this.selectedClass);
24012             this.selections = [];
24013             if(!suppressEvent){
24014                 this.fireEvent("selectionchange", this, this.selections);
24015             }
24016         }
24017     },
24018
24019     /**
24020      * Returns true if the passed node is selected
24021      * @param {HTMLElement/Number} node The node or node index
24022      * @return {Boolean}
24023      */
24024     isSelected : function(node){
24025         var s = this.selections;
24026         if(s.length < 1){
24027             return false;
24028         }
24029         node = this.getNode(node);
24030         return s.indexOf(node) !== -1;
24031     },
24032
24033     /**
24034      * Selects nodes.
24035      * @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
24036      * @param {Boolean} keepExisting (optional) true to keep existing selections
24037      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24038      */
24039     select : function(nodeInfo, keepExisting, suppressEvent){
24040         if(nodeInfo instanceof Array){
24041             if(!keepExisting){
24042                 this.clearSelections(true);
24043             }
24044             for(var i = 0, len = nodeInfo.length; i < len; i++){
24045                 this.select(nodeInfo[i], true, true);
24046             }
24047             return;
24048         } 
24049         var node = this.getNode(nodeInfo);
24050         if(!node || this.isSelected(node)){
24051             return; // already selected.
24052         }
24053         if(!keepExisting){
24054             this.clearSelections(true);
24055         }
24056         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24057             Roo.fly(node).addClass(this.selectedClass);
24058             this.selections.push(node);
24059             if(!suppressEvent){
24060                 this.fireEvent("selectionchange", this, this.selections);
24061             }
24062         }
24063         
24064         
24065     },
24066       /**
24067      * Unselects nodes.
24068      * @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
24069      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24070      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24071      */
24072     unselect : function(nodeInfo, keepExisting, suppressEvent)
24073     {
24074         if(nodeInfo instanceof Array){
24075             Roo.each(this.selections, function(s) {
24076                 this.unselect(s, nodeInfo);
24077             }, this);
24078             return;
24079         }
24080         var node = this.getNode(nodeInfo);
24081         if(!node || !this.isSelected(node)){
24082             Roo.log("not selected");
24083             return; // not selected.
24084         }
24085         // fireevent???
24086         var ns = [];
24087         Roo.each(this.selections, function(s) {
24088             if (s == node ) {
24089                 Roo.fly(node).removeClass(this.selectedClass);
24090
24091                 return;
24092             }
24093             ns.push(s);
24094         },this);
24095         
24096         this.selections= ns;
24097         this.fireEvent("selectionchange", this, this.selections);
24098     },
24099
24100     /**
24101      * Gets a template node.
24102      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24103      * @return {HTMLElement} The node or null if it wasn't found
24104      */
24105     getNode : function(nodeInfo){
24106         if(typeof nodeInfo == "string"){
24107             return document.getElementById(nodeInfo);
24108         }else if(typeof nodeInfo == "number"){
24109             return this.nodes[nodeInfo];
24110         }
24111         return nodeInfo;
24112     },
24113
24114     /**
24115      * Gets a range template nodes.
24116      * @param {Number} startIndex
24117      * @param {Number} endIndex
24118      * @return {Array} An array of nodes
24119      */
24120     getNodes : function(start, end){
24121         var ns = this.nodes;
24122         start = start || 0;
24123         end = typeof end == "undefined" ? ns.length - 1 : end;
24124         var nodes = [];
24125         if(start <= end){
24126             for(var i = start; i <= end; i++){
24127                 nodes.push(ns[i]);
24128             }
24129         } else{
24130             for(var i = start; i >= end; i--){
24131                 nodes.push(ns[i]);
24132             }
24133         }
24134         return nodes;
24135     },
24136
24137     /**
24138      * Finds the index of the passed node
24139      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24140      * @return {Number} The index of the node or -1
24141      */
24142     indexOf : function(node){
24143         node = this.getNode(node);
24144         if(typeof node.nodeIndex == "number"){
24145             return node.nodeIndex;
24146         }
24147         var ns = this.nodes;
24148         for(var i = 0, len = ns.length; i < len; i++){
24149             if(ns[i] == node){
24150                 return i;
24151             }
24152         }
24153         return -1;
24154     }
24155 });
24156 /*
24157  * Based on:
24158  * Ext JS Library 1.1.1
24159  * Copyright(c) 2006-2007, Ext JS, LLC.
24160  *
24161  * Originally Released Under LGPL - original licence link has changed is not relivant.
24162  *
24163  * Fork - LGPL
24164  * <script type="text/javascript">
24165  */
24166
24167 /**
24168  * @class Roo.JsonView
24169  * @extends Roo.View
24170  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24171 <pre><code>
24172 var view = new Roo.JsonView({
24173     container: "my-element",
24174     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24175     multiSelect: true, 
24176     jsonRoot: "data" 
24177 });
24178
24179 // listen for node click?
24180 view.on("click", function(vw, index, node, e){
24181     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24182 });
24183
24184 // direct load of JSON data
24185 view.load("foobar.php");
24186
24187 // Example from my blog list
24188 var tpl = new Roo.Template(
24189     '&lt;div class="entry"&gt;' +
24190     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24191     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24192     "&lt;/div&gt;&lt;hr /&gt;"
24193 );
24194
24195 var moreView = new Roo.JsonView({
24196     container :  "entry-list", 
24197     template : tpl,
24198     jsonRoot: "posts"
24199 });
24200 moreView.on("beforerender", this.sortEntries, this);
24201 moreView.load({
24202     url: "/blog/get-posts.php",
24203     params: "allposts=true",
24204     text: "Loading Blog Entries..."
24205 });
24206 </code></pre>
24207
24208 * Note: old code is supported with arguments : (container, template, config)
24209
24210
24211  * @constructor
24212  * Create a new JsonView
24213  * 
24214  * @param {Object} config The config object
24215  * 
24216  */
24217 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24218     
24219     
24220     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24221
24222     var um = this.el.getUpdateManager();
24223     um.setRenderer(this);
24224     um.on("update", this.onLoad, this);
24225     um.on("failure", this.onLoadException, this);
24226
24227     /**
24228      * @event beforerender
24229      * Fires before rendering of the downloaded JSON data.
24230      * @param {Roo.JsonView} this
24231      * @param {Object} data The JSON data loaded
24232      */
24233     /**
24234      * @event load
24235      * Fires when data is loaded.
24236      * @param {Roo.JsonView} this
24237      * @param {Object} data The JSON data loaded
24238      * @param {Object} response The raw Connect response object
24239      */
24240     /**
24241      * @event loadexception
24242      * Fires when loading fails.
24243      * @param {Roo.JsonView} this
24244      * @param {Object} response The raw Connect response object
24245      */
24246     this.addEvents({
24247         'beforerender' : true,
24248         'load' : true,
24249         'loadexception' : true
24250     });
24251 };
24252 Roo.extend(Roo.JsonView, Roo.View, {
24253     /**
24254      * @type {String} The root property in the loaded JSON object that contains the data
24255      */
24256     jsonRoot : "",
24257
24258     /**
24259      * Refreshes the view.
24260      */
24261     refresh : function(){
24262         this.clearSelections();
24263         this.el.update("");
24264         var html = [];
24265         var o = this.jsonData;
24266         if(o && o.length > 0){
24267             for(var i = 0, len = o.length; i < len; i++){
24268                 var data = this.prepareData(o[i], i, o);
24269                 html[html.length] = this.tpl.apply(data);
24270             }
24271         }else{
24272             html.push(this.emptyText);
24273         }
24274         this.el.update(html.join(""));
24275         this.nodes = this.el.dom.childNodes;
24276         this.updateIndexes(0);
24277     },
24278
24279     /**
24280      * 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.
24281      * @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:
24282      <pre><code>
24283      view.load({
24284          url: "your-url.php",
24285          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24286          callback: yourFunction,
24287          scope: yourObject, //(optional scope)
24288          discardUrl: false,
24289          nocache: false,
24290          text: "Loading...",
24291          timeout: 30,
24292          scripts: false
24293      });
24294      </code></pre>
24295      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24296      * 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.
24297      * @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}
24298      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24299      * @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.
24300      */
24301     load : function(){
24302         var um = this.el.getUpdateManager();
24303         um.update.apply(um, arguments);
24304     },
24305
24306     render : function(el, response){
24307         this.clearSelections();
24308         this.el.update("");
24309         var o;
24310         try{
24311             o = Roo.util.JSON.decode(response.responseText);
24312             if(this.jsonRoot){
24313                 
24314                 o = o[this.jsonRoot];
24315             }
24316         } catch(e){
24317         }
24318         /**
24319          * The current JSON data or null
24320          */
24321         this.jsonData = o;
24322         this.beforeRender();
24323         this.refresh();
24324     },
24325
24326 /**
24327  * Get the number of records in the current JSON dataset
24328  * @return {Number}
24329  */
24330     getCount : function(){
24331         return this.jsonData ? this.jsonData.length : 0;
24332     },
24333
24334 /**
24335  * Returns the JSON object for the specified node(s)
24336  * @param {HTMLElement/Array} node The node or an array of nodes
24337  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24338  * you get the JSON object for the node
24339  */
24340     getNodeData : function(node){
24341         if(node instanceof Array){
24342             var data = [];
24343             for(var i = 0, len = node.length; i < len; i++){
24344                 data.push(this.getNodeData(node[i]));
24345             }
24346             return data;
24347         }
24348         return this.jsonData[this.indexOf(node)] || null;
24349     },
24350
24351     beforeRender : function(){
24352         this.snapshot = this.jsonData;
24353         if(this.sortInfo){
24354             this.sort.apply(this, this.sortInfo);
24355         }
24356         this.fireEvent("beforerender", this, this.jsonData);
24357     },
24358
24359     onLoad : function(el, o){
24360         this.fireEvent("load", this, this.jsonData, o);
24361     },
24362
24363     onLoadException : function(el, o){
24364         this.fireEvent("loadexception", this, o);
24365     },
24366
24367 /**
24368  * Filter the data by a specific property.
24369  * @param {String} property A property on your JSON objects
24370  * @param {String/RegExp} value Either string that the property values
24371  * should start with, or a RegExp to test against the property
24372  */
24373     filter : function(property, value){
24374         if(this.jsonData){
24375             var data = [];
24376             var ss = this.snapshot;
24377             if(typeof value == "string"){
24378                 var vlen = value.length;
24379                 if(vlen == 0){
24380                     this.clearFilter();
24381                     return;
24382                 }
24383                 value = value.toLowerCase();
24384                 for(var i = 0, len = ss.length; i < len; i++){
24385                     var o = ss[i];
24386                     if(o[property].substr(0, vlen).toLowerCase() == value){
24387                         data.push(o);
24388                     }
24389                 }
24390             } else if(value.exec){ // regex?
24391                 for(var i = 0, len = ss.length; i < len; i++){
24392                     var o = ss[i];
24393                     if(value.test(o[property])){
24394                         data.push(o);
24395                     }
24396                 }
24397             } else{
24398                 return;
24399             }
24400             this.jsonData = data;
24401             this.refresh();
24402         }
24403     },
24404
24405 /**
24406  * Filter by a function. The passed function will be called with each
24407  * object in the current dataset. If the function returns true the value is kept,
24408  * otherwise it is filtered.
24409  * @param {Function} fn
24410  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24411  */
24412     filterBy : function(fn, scope){
24413         if(this.jsonData){
24414             var data = [];
24415             var ss = this.snapshot;
24416             for(var i = 0, len = ss.length; i < len; i++){
24417                 var o = ss[i];
24418                 if(fn.call(scope || this, o)){
24419                     data.push(o);
24420                 }
24421             }
24422             this.jsonData = data;
24423             this.refresh();
24424         }
24425     },
24426
24427 /**
24428  * Clears the current filter.
24429  */
24430     clearFilter : function(){
24431         if(this.snapshot && this.jsonData != this.snapshot){
24432             this.jsonData = this.snapshot;
24433             this.refresh();
24434         }
24435     },
24436
24437
24438 /**
24439  * Sorts the data for this view and refreshes it.
24440  * @param {String} property A property on your JSON objects to sort on
24441  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24442  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24443  */
24444     sort : function(property, dir, sortType){
24445         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24446         if(this.jsonData){
24447             var p = property;
24448             var dsc = dir && dir.toLowerCase() == "desc";
24449             var f = function(o1, o2){
24450                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24451                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24452                 ;
24453                 if(v1 < v2){
24454                     return dsc ? +1 : -1;
24455                 } else if(v1 > v2){
24456                     return dsc ? -1 : +1;
24457                 } else{
24458                     return 0;
24459                 }
24460             };
24461             this.jsonData.sort(f);
24462             this.refresh();
24463             if(this.jsonData != this.snapshot){
24464                 this.snapshot.sort(f);
24465             }
24466         }
24467     }
24468 });/*
24469  * Based on:
24470  * Ext JS Library 1.1.1
24471  * Copyright(c) 2006-2007, Ext JS, LLC.
24472  *
24473  * Originally Released Under LGPL - original licence link has changed is not relivant.
24474  *
24475  * Fork - LGPL
24476  * <script type="text/javascript">
24477  */
24478  
24479
24480 /**
24481  * @class Roo.ColorPalette
24482  * @extends Roo.Component
24483  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24484  * Here's an example of typical usage:
24485  * <pre><code>
24486 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24487 cp.render('my-div');
24488
24489 cp.on('select', function(palette, selColor){
24490     // do something with selColor
24491 });
24492 </code></pre>
24493  * @constructor
24494  * Create a new ColorPalette
24495  * @param {Object} config The config object
24496  */
24497 Roo.ColorPalette = function(config){
24498     Roo.ColorPalette.superclass.constructor.call(this, config);
24499     this.addEvents({
24500         /**
24501              * @event select
24502              * Fires when a color is selected
24503              * @param {ColorPalette} this
24504              * @param {String} color The 6-digit color hex code (without the # symbol)
24505              */
24506         select: true
24507     });
24508
24509     if(this.handler){
24510         this.on("select", this.handler, this.scope, true);
24511     }
24512 };
24513 Roo.extend(Roo.ColorPalette, Roo.Component, {
24514     /**
24515      * @cfg {String} itemCls
24516      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24517      */
24518     itemCls : "x-color-palette",
24519     /**
24520      * @cfg {String} value
24521      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24522      * the hex codes are case-sensitive.
24523      */
24524     value : null,
24525     clickEvent:'click',
24526     // private
24527     ctype: "Roo.ColorPalette",
24528
24529     /**
24530      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24531      */
24532     allowReselect : false,
24533
24534     /**
24535      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24536      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24537      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24538      * of colors with the width setting until the box is symmetrical.</p>
24539      * <p>You can override individual colors if needed:</p>
24540      * <pre><code>
24541 var cp = new Roo.ColorPalette();
24542 cp.colors[0] = "FF0000";  // change the first box to red
24543 </code></pre>
24544
24545 Or you can provide a custom array of your own for complete control:
24546 <pre><code>
24547 var cp = new Roo.ColorPalette();
24548 cp.colors = ["000000", "993300", "333300"];
24549 </code></pre>
24550      * @type Array
24551      */
24552     colors : [
24553         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24554         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24555         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24556         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24557         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24558     ],
24559
24560     // private
24561     onRender : function(container, position){
24562         var t = new Roo.MasterTemplate(
24563             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24564         );
24565         var c = this.colors;
24566         for(var i = 0, len = c.length; i < len; i++){
24567             t.add([c[i]]);
24568         }
24569         var el = document.createElement("div");
24570         el.className = this.itemCls;
24571         t.overwrite(el);
24572         container.dom.insertBefore(el, position);
24573         this.el = Roo.get(el);
24574         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24575         if(this.clickEvent != 'click'){
24576             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24577         }
24578     },
24579
24580     // private
24581     afterRender : function(){
24582         Roo.ColorPalette.superclass.afterRender.call(this);
24583         if(this.value){
24584             var s = this.value;
24585             this.value = null;
24586             this.select(s);
24587         }
24588     },
24589
24590     // private
24591     handleClick : function(e, t){
24592         e.preventDefault();
24593         if(!this.disabled){
24594             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24595             this.select(c.toUpperCase());
24596         }
24597     },
24598
24599     /**
24600      * Selects the specified color in the palette (fires the select event)
24601      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24602      */
24603     select : function(color){
24604         color = color.replace("#", "");
24605         if(color != this.value || this.allowReselect){
24606             var el = this.el;
24607             if(this.value){
24608                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24609             }
24610             el.child("a.color-"+color).addClass("x-color-palette-sel");
24611             this.value = color;
24612             this.fireEvent("select", this, color);
24613         }
24614     }
24615 });/*
24616  * Based on:
24617  * Ext JS Library 1.1.1
24618  * Copyright(c) 2006-2007, Ext JS, LLC.
24619  *
24620  * Originally Released Under LGPL - original licence link has changed is not relivant.
24621  *
24622  * Fork - LGPL
24623  * <script type="text/javascript">
24624  */
24625  
24626 /**
24627  * @class Roo.DatePicker
24628  * @extends Roo.Component
24629  * Simple date picker class.
24630  * @constructor
24631  * Create a new DatePicker
24632  * @param {Object} config The config object
24633  */
24634 Roo.DatePicker = function(config){
24635     Roo.DatePicker.superclass.constructor.call(this, config);
24636
24637     this.value = config && config.value ?
24638                  config.value.clearTime() : new Date().clearTime();
24639
24640     this.addEvents({
24641         /**
24642              * @event select
24643              * Fires when a date is selected
24644              * @param {DatePicker} this
24645              * @param {Date} date The selected date
24646              */
24647         'select': true,
24648         /**
24649              * @event monthchange
24650              * Fires when the displayed month changes 
24651              * @param {DatePicker} this
24652              * @param {Date} date The selected month
24653              */
24654         'monthchange': true
24655     });
24656
24657     if(this.handler){
24658         this.on("select", this.handler,  this.scope || this);
24659     }
24660     // build the disabledDatesRE
24661     if(!this.disabledDatesRE && this.disabledDates){
24662         var dd = this.disabledDates;
24663         var re = "(?:";
24664         for(var i = 0; i < dd.length; i++){
24665             re += dd[i];
24666             if(i != dd.length-1) re += "|";
24667         }
24668         this.disabledDatesRE = new RegExp(re + ")");
24669     }
24670 };
24671
24672 Roo.extend(Roo.DatePicker, Roo.Component, {
24673     /**
24674      * @cfg {String} todayText
24675      * The text to display on the button that selects the current date (defaults to "Today")
24676      */
24677     todayText : "Today",
24678     /**
24679      * @cfg {String} okText
24680      * The text to display on the ok button
24681      */
24682     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24683     /**
24684      * @cfg {String} cancelText
24685      * The text to display on the cancel button
24686      */
24687     cancelText : "Cancel",
24688     /**
24689      * @cfg {String} todayTip
24690      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24691      */
24692     todayTip : "{0} (Spacebar)",
24693     /**
24694      * @cfg {Date} minDate
24695      * Minimum allowable date (JavaScript date object, defaults to null)
24696      */
24697     minDate : null,
24698     /**
24699      * @cfg {Date} maxDate
24700      * Maximum allowable date (JavaScript date object, defaults to null)
24701      */
24702     maxDate : null,
24703     /**
24704      * @cfg {String} minText
24705      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24706      */
24707     minText : "This date is before the minimum date",
24708     /**
24709      * @cfg {String} maxText
24710      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24711      */
24712     maxText : "This date is after the maximum date",
24713     /**
24714      * @cfg {String} format
24715      * The default date format string which can be overriden for localization support.  The format must be
24716      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24717      */
24718     format : "m/d/y",
24719     /**
24720      * @cfg {Array} disabledDays
24721      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24722      */
24723     disabledDays : null,
24724     /**
24725      * @cfg {String} disabledDaysText
24726      * The tooltip to display when the date falls on a disabled day (defaults to "")
24727      */
24728     disabledDaysText : "",
24729     /**
24730      * @cfg {RegExp} disabledDatesRE
24731      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24732      */
24733     disabledDatesRE : null,
24734     /**
24735      * @cfg {String} disabledDatesText
24736      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24737      */
24738     disabledDatesText : "",
24739     /**
24740      * @cfg {Boolean} constrainToViewport
24741      * True to constrain the date picker to the viewport (defaults to true)
24742      */
24743     constrainToViewport : true,
24744     /**
24745      * @cfg {Array} monthNames
24746      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24747      */
24748     monthNames : Date.monthNames,
24749     /**
24750      * @cfg {Array} dayNames
24751      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24752      */
24753     dayNames : Date.dayNames,
24754     /**
24755      * @cfg {String} nextText
24756      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24757      */
24758     nextText: 'Next Month (Control+Right)',
24759     /**
24760      * @cfg {String} prevText
24761      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24762      */
24763     prevText: 'Previous Month (Control+Left)',
24764     /**
24765      * @cfg {String} monthYearText
24766      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24767      */
24768     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24769     /**
24770      * @cfg {Number} startDay
24771      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24772      */
24773     startDay : 0,
24774     /**
24775      * @cfg {Bool} showClear
24776      * Show a clear button (usefull for date form elements that can be blank.)
24777      */
24778     
24779     showClear: false,
24780     
24781     /**
24782      * Sets the value of the date field
24783      * @param {Date} value The date to set
24784      */
24785     setValue : function(value){
24786         var old = this.value;
24787         this.value = value.clearTime(true);
24788         if(this.el){
24789             this.update(this.value);
24790         }
24791     },
24792
24793     /**
24794      * Gets the current selected value of the date field
24795      * @return {Date} The selected date
24796      */
24797     getValue : function(){
24798         return this.value;
24799     },
24800
24801     // private
24802     focus : function(){
24803         if(this.el){
24804             this.update(this.activeDate);
24805         }
24806     },
24807
24808     // private
24809     onRender : function(container, position){
24810         var m = [
24811              '<table cellspacing="0">',
24812                 '<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>',
24813                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24814         var dn = this.dayNames;
24815         for(var i = 0; i < 7; i++){
24816             var d = this.startDay+i;
24817             if(d > 6){
24818                 d = d-7;
24819             }
24820             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24821         }
24822         m[m.length] = "</tr></thead><tbody><tr>";
24823         for(var i = 0; i < 42; i++) {
24824             if(i % 7 == 0 && i != 0){
24825                 m[m.length] = "</tr><tr>";
24826             }
24827             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24828         }
24829         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24830             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24831
24832         var el = document.createElement("div");
24833         el.className = "x-date-picker";
24834         el.innerHTML = m.join("");
24835
24836         container.dom.insertBefore(el, position);
24837
24838         this.el = Roo.get(el);
24839         this.eventEl = Roo.get(el.firstChild);
24840
24841         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24842             handler: this.showPrevMonth,
24843             scope: this,
24844             preventDefault:true,
24845             stopDefault:true
24846         });
24847
24848         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24849             handler: this.showNextMonth,
24850             scope: this,
24851             preventDefault:true,
24852             stopDefault:true
24853         });
24854
24855         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24856
24857         this.monthPicker = this.el.down('div.x-date-mp');
24858         this.monthPicker.enableDisplayMode('block');
24859         
24860         var kn = new Roo.KeyNav(this.eventEl, {
24861             "left" : function(e){
24862                 e.ctrlKey ?
24863                     this.showPrevMonth() :
24864                     this.update(this.activeDate.add("d", -1));
24865             },
24866
24867             "right" : function(e){
24868                 e.ctrlKey ?
24869                     this.showNextMonth() :
24870                     this.update(this.activeDate.add("d", 1));
24871             },
24872
24873             "up" : function(e){
24874                 e.ctrlKey ?
24875                     this.showNextYear() :
24876                     this.update(this.activeDate.add("d", -7));
24877             },
24878
24879             "down" : function(e){
24880                 e.ctrlKey ?
24881                     this.showPrevYear() :
24882                     this.update(this.activeDate.add("d", 7));
24883             },
24884
24885             "pageUp" : function(e){
24886                 this.showNextMonth();
24887             },
24888
24889             "pageDown" : function(e){
24890                 this.showPrevMonth();
24891             },
24892
24893             "enter" : function(e){
24894                 e.stopPropagation();
24895                 return true;
24896             },
24897
24898             scope : this
24899         });
24900
24901         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24902
24903         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24904
24905         this.el.unselectable();
24906         
24907         this.cells = this.el.select("table.x-date-inner tbody td");
24908         this.textNodes = this.el.query("table.x-date-inner tbody span");
24909
24910         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24911             text: "&#160;",
24912             tooltip: this.monthYearText
24913         });
24914
24915         this.mbtn.on('click', this.showMonthPicker, this);
24916         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24917
24918
24919         var today = (new Date()).dateFormat(this.format);
24920         
24921         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24922         if (this.showClear) {
24923             baseTb.add( new Roo.Toolbar.Fill());
24924         }
24925         baseTb.add({
24926             text: String.format(this.todayText, today),
24927             tooltip: String.format(this.todayTip, today),
24928             handler: this.selectToday,
24929             scope: this
24930         });
24931         
24932         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24933             
24934         //});
24935         if (this.showClear) {
24936             
24937             baseTb.add( new Roo.Toolbar.Fill());
24938             baseTb.add({
24939                 text: '&#160;',
24940                 cls: 'x-btn-icon x-btn-clear',
24941                 handler: function() {
24942                     //this.value = '';
24943                     this.fireEvent("select", this, '');
24944                 },
24945                 scope: this
24946             });
24947         }
24948         
24949         
24950         if(Roo.isIE){
24951             this.el.repaint();
24952         }
24953         this.update(this.value);
24954     },
24955
24956     createMonthPicker : function(){
24957         if(!this.monthPicker.dom.firstChild){
24958             var buf = ['<table border="0" cellspacing="0">'];
24959             for(var i = 0; i < 6; i++){
24960                 buf.push(
24961                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24962                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24963                     i == 0 ?
24964                     '<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>' :
24965                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24966                 );
24967             }
24968             buf.push(
24969                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24970                     this.okText,
24971                     '</button><button type="button" class="x-date-mp-cancel">',
24972                     this.cancelText,
24973                     '</button></td></tr>',
24974                 '</table>'
24975             );
24976             this.monthPicker.update(buf.join(''));
24977             this.monthPicker.on('click', this.onMonthClick, this);
24978             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24979
24980             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24981             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24982
24983             this.mpMonths.each(function(m, a, i){
24984                 i += 1;
24985                 if((i%2) == 0){
24986                     m.dom.xmonth = 5 + Math.round(i * .5);
24987                 }else{
24988                     m.dom.xmonth = Math.round((i-1) * .5);
24989                 }
24990             });
24991         }
24992     },
24993
24994     showMonthPicker : function(){
24995         this.createMonthPicker();
24996         var size = this.el.getSize();
24997         this.monthPicker.setSize(size);
24998         this.monthPicker.child('table').setSize(size);
24999
25000         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25001         this.updateMPMonth(this.mpSelMonth);
25002         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25003         this.updateMPYear(this.mpSelYear);
25004
25005         this.monthPicker.slideIn('t', {duration:.2});
25006     },
25007
25008     updateMPYear : function(y){
25009         this.mpyear = y;
25010         var ys = this.mpYears.elements;
25011         for(var i = 1; i <= 10; i++){
25012             var td = ys[i-1], y2;
25013             if((i%2) == 0){
25014                 y2 = y + Math.round(i * .5);
25015                 td.firstChild.innerHTML = y2;
25016                 td.xyear = y2;
25017             }else{
25018                 y2 = y - (5-Math.round(i * .5));
25019                 td.firstChild.innerHTML = y2;
25020                 td.xyear = y2;
25021             }
25022             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25023         }
25024     },
25025
25026     updateMPMonth : function(sm){
25027         this.mpMonths.each(function(m, a, i){
25028             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25029         });
25030     },
25031
25032     selectMPMonth: function(m){
25033         
25034     },
25035
25036     onMonthClick : function(e, t){
25037         e.stopEvent();
25038         var el = new Roo.Element(t), pn;
25039         if(el.is('button.x-date-mp-cancel')){
25040             this.hideMonthPicker();
25041         }
25042         else if(el.is('button.x-date-mp-ok')){
25043             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25044             this.hideMonthPicker();
25045         }
25046         else if(pn = el.up('td.x-date-mp-month', 2)){
25047             this.mpMonths.removeClass('x-date-mp-sel');
25048             pn.addClass('x-date-mp-sel');
25049             this.mpSelMonth = pn.dom.xmonth;
25050         }
25051         else if(pn = el.up('td.x-date-mp-year', 2)){
25052             this.mpYears.removeClass('x-date-mp-sel');
25053             pn.addClass('x-date-mp-sel');
25054             this.mpSelYear = pn.dom.xyear;
25055         }
25056         else if(el.is('a.x-date-mp-prev')){
25057             this.updateMPYear(this.mpyear-10);
25058         }
25059         else if(el.is('a.x-date-mp-next')){
25060             this.updateMPYear(this.mpyear+10);
25061         }
25062     },
25063
25064     onMonthDblClick : function(e, t){
25065         e.stopEvent();
25066         var el = new Roo.Element(t), pn;
25067         if(pn = el.up('td.x-date-mp-month', 2)){
25068             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25069             this.hideMonthPicker();
25070         }
25071         else if(pn = el.up('td.x-date-mp-year', 2)){
25072             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25073             this.hideMonthPicker();
25074         }
25075     },
25076
25077     hideMonthPicker : function(disableAnim){
25078         if(this.monthPicker){
25079             if(disableAnim === true){
25080                 this.monthPicker.hide();
25081             }else{
25082                 this.monthPicker.slideOut('t', {duration:.2});
25083             }
25084         }
25085     },
25086
25087     // private
25088     showPrevMonth : function(e){
25089         this.update(this.activeDate.add("mo", -1));
25090     },
25091
25092     // private
25093     showNextMonth : function(e){
25094         this.update(this.activeDate.add("mo", 1));
25095     },
25096
25097     // private
25098     showPrevYear : function(){
25099         this.update(this.activeDate.add("y", -1));
25100     },
25101
25102     // private
25103     showNextYear : function(){
25104         this.update(this.activeDate.add("y", 1));
25105     },
25106
25107     // private
25108     handleMouseWheel : function(e){
25109         var delta = e.getWheelDelta();
25110         if(delta > 0){
25111             this.showPrevMonth();
25112             e.stopEvent();
25113         } else if(delta < 0){
25114             this.showNextMonth();
25115             e.stopEvent();
25116         }
25117     },
25118
25119     // private
25120     handleDateClick : function(e, t){
25121         e.stopEvent();
25122         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25123             this.setValue(new Date(t.dateValue));
25124             this.fireEvent("select", this, this.value);
25125         }
25126     },
25127
25128     // private
25129     selectToday : function(){
25130         this.setValue(new Date().clearTime());
25131         this.fireEvent("select", this, this.value);
25132     },
25133
25134     // private
25135     update : function(date)
25136     {
25137         var vd = this.activeDate;
25138         this.activeDate = date;
25139         if(vd && this.el){
25140             var t = date.getTime();
25141             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25142                 this.cells.removeClass("x-date-selected");
25143                 this.cells.each(function(c){
25144                    if(c.dom.firstChild.dateValue == t){
25145                        c.addClass("x-date-selected");
25146                        setTimeout(function(){
25147                             try{c.dom.firstChild.focus();}catch(e){}
25148                        }, 50);
25149                        return false;
25150                    }
25151                 });
25152                 return;
25153             }
25154         }
25155         
25156         var days = date.getDaysInMonth();
25157         var firstOfMonth = date.getFirstDateOfMonth();
25158         var startingPos = firstOfMonth.getDay()-this.startDay;
25159
25160         if(startingPos <= this.startDay){
25161             startingPos += 7;
25162         }
25163
25164         var pm = date.add("mo", -1);
25165         var prevStart = pm.getDaysInMonth()-startingPos;
25166
25167         var cells = this.cells.elements;
25168         var textEls = this.textNodes;
25169         days += startingPos;
25170
25171         // convert everything to numbers so it's fast
25172         var day = 86400000;
25173         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25174         var today = new Date().clearTime().getTime();
25175         var sel = date.clearTime().getTime();
25176         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25177         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25178         var ddMatch = this.disabledDatesRE;
25179         var ddText = this.disabledDatesText;
25180         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25181         var ddaysText = this.disabledDaysText;
25182         var format = this.format;
25183
25184         var setCellClass = function(cal, cell){
25185             cell.title = "";
25186             var t = d.getTime();
25187             cell.firstChild.dateValue = t;
25188             if(t == today){
25189                 cell.className += " x-date-today";
25190                 cell.title = cal.todayText;
25191             }
25192             if(t == sel){
25193                 cell.className += " x-date-selected";
25194                 setTimeout(function(){
25195                     try{cell.firstChild.focus();}catch(e){}
25196                 }, 50);
25197             }
25198             // disabling
25199             if(t < min) {
25200                 cell.className = " x-date-disabled";
25201                 cell.title = cal.minText;
25202                 return;
25203             }
25204             if(t > max) {
25205                 cell.className = " x-date-disabled";
25206                 cell.title = cal.maxText;
25207                 return;
25208             }
25209             if(ddays){
25210                 if(ddays.indexOf(d.getDay()) != -1){
25211                     cell.title = ddaysText;
25212                     cell.className = " x-date-disabled";
25213                 }
25214             }
25215             if(ddMatch && format){
25216                 var fvalue = d.dateFormat(format);
25217                 if(ddMatch.test(fvalue)){
25218                     cell.title = ddText.replace("%0", fvalue);
25219                     cell.className = " x-date-disabled";
25220                 }
25221             }
25222         };
25223
25224         var i = 0;
25225         for(; i < startingPos; i++) {
25226             textEls[i].innerHTML = (++prevStart);
25227             d.setDate(d.getDate()+1);
25228             cells[i].className = "x-date-prevday";
25229             setCellClass(this, cells[i]);
25230         }
25231         for(; i < days; i++){
25232             intDay = i - startingPos + 1;
25233             textEls[i].innerHTML = (intDay);
25234             d.setDate(d.getDate()+1);
25235             cells[i].className = "x-date-active";
25236             setCellClass(this, cells[i]);
25237         }
25238         var extraDays = 0;
25239         for(; i < 42; i++) {
25240              textEls[i].innerHTML = (++extraDays);
25241              d.setDate(d.getDate()+1);
25242              cells[i].className = "x-date-nextday";
25243              setCellClass(this, cells[i]);
25244         }
25245
25246         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25247         this.fireEvent('monthchange', this, date);
25248         
25249         if(!this.internalRender){
25250             var main = this.el.dom.firstChild;
25251             var w = main.offsetWidth;
25252             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25253             Roo.fly(main).setWidth(w);
25254             this.internalRender = true;
25255             // opera does not respect the auto grow header center column
25256             // then, after it gets a width opera refuses to recalculate
25257             // without a second pass
25258             if(Roo.isOpera && !this.secondPass){
25259                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25260                 this.secondPass = true;
25261                 this.update.defer(10, this, [date]);
25262             }
25263         }
25264         
25265         
25266     }
25267 });        /*
25268  * Based on:
25269  * Ext JS Library 1.1.1
25270  * Copyright(c) 2006-2007, Ext JS, LLC.
25271  *
25272  * Originally Released Under LGPL - original licence link has changed is not relivant.
25273  *
25274  * Fork - LGPL
25275  * <script type="text/javascript">
25276  */
25277 /**
25278  * @class Roo.TabPanel
25279  * @extends Roo.util.Observable
25280  * A lightweight tab container.
25281  * <br><br>
25282  * Usage:
25283  * <pre><code>
25284 // basic tabs 1, built from existing content
25285 var tabs = new Roo.TabPanel("tabs1");
25286 tabs.addTab("script", "View Script");
25287 tabs.addTab("markup", "View Markup");
25288 tabs.activate("script");
25289
25290 // more advanced tabs, built from javascript
25291 var jtabs = new Roo.TabPanel("jtabs");
25292 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25293
25294 // set up the UpdateManager
25295 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25296 var updater = tab2.getUpdateManager();
25297 updater.setDefaultUrl("ajax1.htm");
25298 tab2.on('activate', updater.refresh, updater, true);
25299
25300 // Use setUrl for Ajax loading
25301 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25302 tab3.setUrl("ajax2.htm", null, true);
25303
25304 // Disabled tab
25305 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25306 tab4.disable();
25307
25308 jtabs.activate("jtabs-1");
25309  * </code></pre>
25310  * @constructor
25311  * Create a new TabPanel.
25312  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25313  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25314  */
25315 Roo.TabPanel = function(container, config){
25316     /**
25317     * The container element for this TabPanel.
25318     * @type Roo.Element
25319     */
25320     this.el = Roo.get(container, true);
25321     if(config){
25322         if(typeof config == "boolean"){
25323             this.tabPosition = config ? "bottom" : "top";
25324         }else{
25325             Roo.apply(this, config);
25326         }
25327     }
25328     if(this.tabPosition == "bottom"){
25329         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25330         this.el.addClass("x-tabs-bottom");
25331     }
25332     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25333     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25334     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25335     if(Roo.isIE){
25336         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25337     }
25338     if(this.tabPosition != "bottom"){
25339         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25340          * @type Roo.Element
25341          */
25342         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25343         this.el.addClass("x-tabs-top");
25344     }
25345     this.items = [];
25346
25347     this.bodyEl.setStyle("position", "relative");
25348
25349     this.active = null;
25350     this.activateDelegate = this.activate.createDelegate(this);
25351
25352     this.addEvents({
25353         /**
25354          * @event tabchange
25355          * Fires when the active tab changes
25356          * @param {Roo.TabPanel} this
25357          * @param {Roo.TabPanelItem} activePanel The new active tab
25358          */
25359         "tabchange": true,
25360         /**
25361          * @event beforetabchange
25362          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25363          * @param {Roo.TabPanel} this
25364          * @param {Object} e Set cancel to true on this object to cancel the tab change
25365          * @param {Roo.TabPanelItem} tab The tab being changed to
25366          */
25367         "beforetabchange" : true
25368     });
25369
25370     Roo.EventManager.onWindowResize(this.onResize, this);
25371     this.cpad = this.el.getPadding("lr");
25372     this.hiddenCount = 0;
25373
25374
25375     // toolbar on the tabbar support...
25376     if (this.toolbar) {
25377         var tcfg = this.toolbar;
25378         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25379         this.toolbar = new Roo.Toolbar(tcfg);
25380         if (Roo.isSafari) {
25381             var tbl = tcfg.container.child('table', true);
25382             tbl.setAttribute('width', '100%');
25383         }
25384         
25385     }
25386    
25387
25388
25389     Roo.TabPanel.superclass.constructor.call(this);
25390 };
25391
25392 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25393     /*
25394      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25395      */
25396     tabPosition : "top",
25397     /*
25398      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25399      */
25400     currentTabWidth : 0,
25401     /*
25402      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25403      */
25404     minTabWidth : 40,
25405     /*
25406      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25407      */
25408     maxTabWidth : 250,
25409     /*
25410      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25411      */
25412     preferredTabWidth : 175,
25413     /*
25414      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25415      */
25416     resizeTabs : false,
25417     /*
25418      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25419      */
25420     monitorResize : true,
25421     /*
25422      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25423      */
25424     toolbar : false,
25425
25426     /**
25427      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25428      * @param {String} id The id of the div to use <b>or create</b>
25429      * @param {String} text The text for the tab
25430      * @param {String} content (optional) Content to put in the TabPanelItem body
25431      * @param {Boolean} closable (optional) True to create a close icon on the tab
25432      * @return {Roo.TabPanelItem} The created TabPanelItem
25433      */
25434     addTab : function(id, text, content, closable){
25435         var item = new Roo.TabPanelItem(this, id, text, closable);
25436         this.addTabItem(item);
25437         if(content){
25438             item.setContent(content);
25439         }
25440         return item;
25441     },
25442
25443     /**
25444      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25445      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25446      * @return {Roo.TabPanelItem}
25447      */
25448     getTab : function(id){
25449         return this.items[id];
25450     },
25451
25452     /**
25453      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25454      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25455      */
25456     hideTab : function(id){
25457         var t = this.items[id];
25458         if(!t.isHidden()){
25459            t.setHidden(true);
25460            this.hiddenCount++;
25461            this.autoSizeTabs();
25462         }
25463     },
25464
25465     /**
25466      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25467      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25468      */
25469     unhideTab : function(id){
25470         var t = this.items[id];
25471         if(t.isHidden()){
25472            t.setHidden(false);
25473            this.hiddenCount--;
25474            this.autoSizeTabs();
25475         }
25476     },
25477
25478     /**
25479      * Adds an existing {@link Roo.TabPanelItem}.
25480      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25481      */
25482     addTabItem : function(item){
25483         this.items[item.id] = item;
25484         this.items.push(item);
25485         if(this.resizeTabs){
25486            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25487            this.autoSizeTabs();
25488         }else{
25489             item.autoSize();
25490         }
25491     },
25492
25493     /**
25494      * Removes a {@link Roo.TabPanelItem}.
25495      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25496      */
25497     removeTab : function(id){
25498         var items = this.items;
25499         var tab = items[id];
25500         if(!tab) { return; }
25501         var index = items.indexOf(tab);
25502         if(this.active == tab && items.length > 1){
25503             var newTab = this.getNextAvailable(index);
25504             if(newTab) {
25505                 newTab.activate();
25506             }
25507         }
25508         this.stripEl.dom.removeChild(tab.pnode.dom);
25509         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25510             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25511         }
25512         items.splice(index, 1);
25513         delete this.items[tab.id];
25514         tab.fireEvent("close", tab);
25515         tab.purgeListeners();
25516         this.autoSizeTabs();
25517     },
25518
25519     getNextAvailable : function(start){
25520         var items = this.items;
25521         var index = start;
25522         // look for a next tab that will slide over to
25523         // replace the one being removed
25524         while(index < items.length){
25525             var item = items[++index];
25526             if(item && !item.isHidden()){
25527                 return item;
25528             }
25529         }
25530         // if one isn't found select the previous tab (on the left)
25531         index = start;
25532         while(index >= 0){
25533             var item = items[--index];
25534             if(item && !item.isHidden()){
25535                 return item;
25536             }
25537         }
25538         return null;
25539     },
25540
25541     /**
25542      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25543      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25544      */
25545     disableTab : function(id){
25546         var tab = this.items[id];
25547         if(tab && this.active != tab){
25548             tab.disable();
25549         }
25550     },
25551
25552     /**
25553      * Enables a {@link Roo.TabPanelItem} that is disabled.
25554      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25555      */
25556     enableTab : function(id){
25557         var tab = this.items[id];
25558         tab.enable();
25559     },
25560
25561     /**
25562      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25563      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25564      * @return {Roo.TabPanelItem} The TabPanelItem.
25565      */
25566     activate : function(id){
25567         var tab = this.items[id];
25568         if(!tab){
25569             return null;
25570         }
25571         if(tab == this.active || tab.disabled){
25572             return tab;
25573         }
25574         var e = {};
25575         this.fireEvent("beforetabchange", this, e, tab);
25576         if(e.cancel !== true && !tab.disabled){
25577             if(this.active){
25578                 this.active.hide();
25579             }
25580             this.active = this.items[id];
25581             this.active.show();
25582             this.fireEvent("tabchange", this, this.active);
25583         }
25584         return tab;
25585     },
25586
25587     /**
25588      * Gets the active {@link Roo.TabPanelItem}.
25589      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25590      */
25591     getActiveTab : function(){
25592         return this.active;
25593     },
25594
25595     /**
25596      * Updates the tab body element to fit the height of the container element
25597      * for overflow scrolling
25598      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25599      */
25600     syncHeight : function(targetHeight){
25601         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25602         var bm = this.bodyEl.getMargins();
25603         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25604         this.bodyEl.setHeight(newHeight);
25605         return newHeight;
25606     },
25607
25608     onResize : function(){
25609         if(this.monitorResize){
25610             this.autoSizeTabs();
25611         }
25612     },
25613
25614     /**
25615      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25616      */
25617     beginUpdate : function(){
25618         this.updating = true;
25619     },
25620
25621     /**
25622      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25623      */
25624     endUpdate : function(){
25625         this.updating = false;
25626         this.autoSizeTabs();
25627     },
25628
25629     /**
25630      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25631      */
25632     autoSizeTabs : function(){
25633         var count = this.items.length;
25634         var vcount = count - this.hiddenCount;
25635         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25636         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25637         var availWidth = Math.floor(w / vcount);
25638         var b = this.stripBody;
25639         if(b.getWidth() > w){
25640             var tabs = this.items;
25641             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25642             if(availWidth < this.minTabWidth){
25643                 /*if(!this.sleft){    // incomplete scrolling code
25644                     this.createScrollButtons();
25645                 }
25646                 this.showScroll();
25647                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25648             }
25649         }else{
25650             if(this.currentTabWidth < this.preferredTabWidth){
25651                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25652             }
25653         }
25654     },
25655
25656     /**
25657      * Returns the number of tabs in this TabPanel.
25658      * @return {Number}
25659      */
25660      getCount : function(){
25661          return this.items.length;
25662      },
25663
25664     /**
25665      * Resizes all the tabs to the passed width
25666      * @param {Number} The new width
25667      */
25668     setTabWidth : function(width){
25669         this.currentTabWidth = width;
25670         for(var i = 0, len = this.items.length; i < len; i++) {
25671                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25672         }
25673     },
25674
25675     /**
25676      * Destroys this TabPanel
25677      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25678      */
25679     destroy : function(removeEl){
25680         Roo.EventManager.removeResizeListener(this.onResize, this);
25681         for(var i = 0, len = this.items.length; i < len; i++){
25682             this.items[i].purgeListeners();
25683         }
25684         if(removeEl === true){
25685             this.el.update("");
25686             this.el.remove();
25687         }
25688     }
25689 });
25690
25691 /**
25692  * @class Roo.TabPanelItem
25693  * @extends Roo.util.Observable
25694  * Represents an individual item (tab plus body) in a TabPanel.
25695  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25696  * @param {String} id The id of this TabPanelItem
25697  * @param {String} text The text for the tab of this TabPanelItem
25698  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25699  */
25700 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25701     /**
25702      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25703      * @type Roo.TabPanel
25704      */
25705     this.tabPanel = tabPanel;
25706     /**
25707      * The id for this TabPanelItem
25708      * @type String
25709      */
25710     this.id = id;
25711     /** @private */
25712     this.disabled = false;
25713     /** @private */
25714     this.text = text;
25715     /** @private */
25716     this.loaded = false;
25717     this.closable = closable;
25718
25719     /**
25720      * The body element for this TabPanelItem.
25721      * @type Roo.Element
25722      */
25723     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25724     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25725     this.bodyEl.setStyle("display", "block");
25726     this.bodyEl.setStyle("zoom", "1");
25727     this.hideAction();
25728
25729     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25730     /** @private */
25731     this.el = Roo.get(els.el, true);
25732     this.inner = Roo.get(els.inner, true);
25733     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25734     this.pnode = Roo.get(els.el.parentNode, true);
25735     this.el.on("mousedown", this.onTabMouseDown, this);
25736     this.el.on("click", this.onTabClick, this);
25737     /** @private */
25738     if(closable){
25739         var c = Roo.get(els.close, true);
25740         c.dom.title = this.closeText;
25741         c.addClassOnOver("close-over");
25742         c.on("click", this.closeClick, this);
25743      }
25744
25745     this.addEvents({
25746          /**
25747          * @event activate
25748          * Fires when this tab becomes the active tab.
25749          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25750          * @param {Roo.TabPanelItem} this
25751          */
25752         "activate": true,
25753         /**
25754          * @event beforeclose
25755          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25756          * @param {Roo.TabPanelItem} this
25757          * @param {Object} e Set cancel to true on this object to cancel the close.
25758          */
25759         "beforeclose": true,
25760         /**
25761          * @event close
25762          * Fires when this tab is closed.
25763          * @param {Roo.TabPanelItem} this
25764          */
25765          "close": true,
25766         /**
25767          * @event deactivate
25768          * Fires when this tab is no longer the active tab.
25769          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25770          * @param {Roo.TabPanelItem} this
25771          */
25772          "deactivate" : true
25773     });
25774     this.hidden = false;
25775
25776     Roo.TabPanelItem.superclass.constructor.call(this);
25777 };
25778
25779 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25780     purgeListeners : function(){
25781        Roo.util.Observable.prototype.purgeListeners.call(this);
25782        this.el.removeAllListeners();
25783     },
25784     /**
25785      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25786      */
25787     show : function(){
25788         this.pnode.addClass("on");
25789         this.showAction();
25790         if(Roo.isOpera){
25791             this.tabPanel.stripWrap.repaint();
25792         }
25793         this.fireEvent("activate", this.tabPanel, this);
25794     },
25795
25796     /**
25797      * Returns true if this tab is the active tab.
25798      * @return {Boolean}
25799      */
25800     isActive : function(){
25801         return this.tabPanel.getActiveTab() == this;
25802     },
25803
25804     /**
25805      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25806      */
25807     hide : function(){
25808         this.pnode.removeClass("on");
25809         this.hideAction();
25810         this.fireEvent("deactivate", this.tabPanel, this);
25811     },
25812
25813     hideAction : function(){
25814         this.bodyEl.hide();
25815         this.bodyEl.setStyle("position", "absolute");
25816         this.bodyEl.setLeft("-20000px");
25817         this.bodyEl.setTop("-20000px");
25818     },
25819
25820     showAction : function(){
25821         this.bodyEl.setStyle("position", "relative");
25822         this.bodyEl.setTop("");
25823         this.bodyEl.setLeft("");
25824         this.bodyEl.show();
25825     },
25826
25827     /**
25828      * Set the tooltip for the tab.
25829      * @param {String} tooltip The tab's tooltip
25830      */
25831     setTooltip : function(text){
25832         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25833             this.textEl.dom.qtip = text;
25834             this.textEl.dom.removeAttribute('title');
25835         }else{
25836             this.textEl.dom.title = text;
25837         }
25838     },
25839
25840     onTabClick : function(e){
25841         e.preventDefault();
25842         this.tabPanel.activate(this.id);
25843     },
25844
25845     onTabMouseDown : function(e){
25846         e.preventDefault();
25847         this.tabPanel.activate(this.id);
25848     },
25849
25850     getWidth : function(){
25851         return this.inner.getWidth();
25852     },
25853
25854     setWidth : function(width){
25855         var iwidth = width - this.pnode.getPadding("lr");
25856         this.inner.setWidth(iwidth);
25857         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25858         this.pnode.setWidth(width);
25859     },
25860
25861     /**
25862      * Show or hide the tab
25863      * @param {Boolean} hidden True to hide or false to show.
25864      */
25865     setHidden : function(hidden){
25866         this.hidden = hidden;
25867         this.pnode.setStyle("display", hidden ? "none" : "");
25868     },
25869
25870     /**
25871      * Returns true if this tab is "hidden"
25872      * @return {Boolean}
25873      */
25874     isHidden : function(){
25875         return this.hidden;
25876     },
25877
25878     /**
25879      * Returns the text for this tab
25880      * @return {String}
25881      */
25882     getText : function(){
25883         return this.text;
25884     },
25885
25886     autoSize : function(){
25887         //this.el.beginMeasure();
25888         this.textEl.setWidth(1);
25889         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25890         //this.el.endMeasure();
25891     },
25892
25893     /**
25894      * Sets the text for the tab (Note: this also sets the tooltip text)
25895      * @param {String} text The tab's text and tooltip
25896      */
25897     setText : function(text){
25898         this.text = text;
25899         this.textEl.update(text);
25900         this.setTooltip(text);
25901         if(!this.tabPanel.resizeTabs){
25902             this.autoSize();
25903         }
25904     },
25905     /**
25906      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25907      */
25908     activate : function(){
25909         this.tabPanel.activate(this.id);
25910     },
25911
25912     /**
25913      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25914      */
25915     disable : function(){
25916         if(this.tabPanel.active != this){
25917             this.disabled = true;
25918             this.pnode.addClass("disabled");
25919         }
25920     },
25921
25922     /**
25923      * Enables this TabPanelItem if it was previously disabled.
25924      */
25925     enable : function(){
25926         this.disabled = false;
25927         this.pnode.removeClass("disabled");
25928     },
25929
25930     /**
25931      * Sets the content for this TabPanelItem.
25932      * @param {String} content The content
25933      * @param {Boolean} loadScripts true to look for and load scripts
25934      */
25935     setContent : function(content, loadScripts){
25936         this.bodyEl.update(content, loadScripts);
25937     },
25938
25939     /**
25940      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25941      * @return {Roo.UpdateManager} The UpdateManager
25942      */
25943     getUpdateManager : function(){
25944         return this.bodyEl.getUpdateManager();
25945     },
25946
25947     /**
25948      * Set a URL to be used to load the content for this TabPanelItem.
25949      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25950      * @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)
25951      * @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)
25952      * @return {Roo.UpdateManager} The UpdateManager
25953      */
25954     setUrl : function(url, params, loadOnce){
25955         if(this.refreshDelegate){
25956             this.un('activate', this.refreshDelegate);
25957         }
25958         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25959         this.on("activate", this.refreshDelegate);
25960         return this.bodyEl.getUpdateManager();
25961     },
25962
25963     /** @private */
25964     _handleRefresh : function(url, params, loadOnce){
25965         if(!loadOnce || !this.loaded){
25966             var updater = this.bodyEl.getUpdateManager();
25967             updater.update(url, params, this._setLoaded.createDelegate(this));
25968         }
25969     },
25970
25971     /**
25972      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25973      *   Will fail silently if the setUrl method has not been called.
25974      *   This does not activate the panel, just updates its content.
25975      */
25976     refresh : function(){
25977         if(this.refreshDelegate){
25978            this.loaded = false;
25979            this.refreshDelegate();
25980         }
25981     },
25982
25983     /** @private */
25984     _setLoaded : function(){
25985         this.loaded = true;
25986     },
25987
25988     /** @private */
25989     closeClick : function(e){
25990         var o = {};
25991         e.stopEvent();
25992         this.fireEvent("beforeclose", this, o);
25993         if(o.cancel !== true){
25994             this.tabPanel.removeTab(this.id);
25995         }
25996     },
25997     /**
25998      * The text displayed in the tooltip for the close icon.
25999      * @type String
26000      */
26001     closeText : "Close this tab"
26002 });
26003
26004 /** @private */
26005 Roo.TabPanel.prototype.createStrip = function(container){
26006     var strip = document.createElement("div");
26007     strip.className = "x-tabs-wrap";
26008     container.appendChild(strip);
26009     return strip;
26010 };
26011 /** @private */
26012 Roo.TabPanel.prototype.createStripList = function(strip){
26013     // div wrapper for retard IE
26014     // returns the "tr" element.
26015     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26016         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26017         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26018     return strip.firstChild.firstChild.firstChild.firstChild;
26019 };
26020 /** @private */
26021 Roo.TabPanel.prototype.createBody = function(container){
26022     var body = document.createElement("div");
26023     Roo.id(body, "tab-body");
26024     Roo.fly(body).addClass("x-tabs-body");
26025     container.appendChild(body);
26026     return body;
26027 };
26028 /** @private */
26029 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26030     var body = Roo.getDom(id);
26031     if(!body){
26032         body = document.createElement("div");
26033         body.id = id;
26034     }
26035     Roo.fly(body).addClass("x-tabs-item-body");
26036     bodyEl.insertBefore(body, bodyEl.firstChild);
26037     return body;
26038 };
26039 /** @private */
26040 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26041     var td = document.createElement("td");
26042     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26043     //stripEl.appendChild(td);
26044     if(closable){
26045         td.className = "x-tabs-closable";
26046         if(!this.closeTpl){
26047             this.closeTpl = new Roo.Template(
26048                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26049                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26050                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26051             );
26052         }
26053         var el = this.closeTpl.overwrite(td, {"text": text});
26054         var close = el.getElementsByTagName("div")[0];
26055         var inner = el.getElementsByTagName("em")[0];
26056         return {"el": el, "close": close, "inner": inner};
26057     } else {
26058         if(!this.tabTpl){
26059             this.tabTpl = new Roo.Template(
26060                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26061                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26062             );
26063         }
26064         var el = this.tabTpl.overwrite(td, {"text": text});
26065         var inner = el.getElementsByTagName("em")[0];
26066         return {"el": el, "inner": inner};
26067     }
26068 };/*
26069  * Based on:
26070  * Ext JS Library 1.1.1
26071  * Copyright(c) 2006-2007, Ext JS, LLC.
26072  *
26073  * Originally Released Under LGPL - original licence link has changed is not relivant.
26074  *
26075  * Fork - LGPL
26076  * <script type="text/javascript">
26077  */
26078
26079 /**
26080  * @class Roo.Button
26081  * @extends Roo.util.Observable
26082  * Simple Button class
26083  * @cfg {String} text The button text
26084  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26085  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26086  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26087  * @cfg {Object} scope The scope of the handler
26088  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26089  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26090  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26091  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26092  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26093  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26094    applies if enableToggle = true)
26095  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26096  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26097   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26098  * @constructor
26099  * Create a new button
26100  * @param {Object} config The config object
26101  */
26102 Roo.Button = function(renderTo, config)
26103 {
26104     if (!config) {
26105         config = renderTo;
26106         renderTo = config.renderTo || false;
26107     }
26108     
26109     Roo.apply(this, config);
26110     this.addEvents({
26111         /**
26112              * @event click
26113              * Fires when this button is clicked
26114              * @param {Button} this
26115              * @param {EventObject} e The click event
26116              */
26117             "click" : true,
26118         /**
26119              * @event toggle
26120              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26121              * @param {Button} this
26122              * @param {Boolean} pressed
26123              */
26124             "toggle" : true,
26125         /**
26126              * @event mouseover
26127              * Fires when the mouse hovers over the button
26128              * @param {Button} this
26129              * @param {Event} e The event object
26130              */
26131         'mouseover' : true,
26132         /**
26133              * @event mouseout
26134              * Fires when the mouse exits the button
26135              * @param {Button} this
26136              * @param {Event} e The event object
26137              */
26138         'mouseout': true,
26139          /**
26140              * @event render
26141              * Fires when the button is rendered
26142              * @param {Button} this
26143              */
26144         'render': true
26145     });
26146     if(this.menu){
26147         this.menu = Roo.menu.MenuMgr.get(this.menu);
26148     }
26149     // register listeners first!!  - so render can be captured..
26150     Roo.util.Observable.call(this);
26151     if(renderTo){
26152         this.render(renderTo);
26153     }
26154     
26155   
26156 };
26157
26158 Roo.extend(Roo.Button, Roo.util.Observable, {
26159     /**
26160      * 
26161      */
26162     
26163     /**
26164      * Read-only. True if this button is hidden
26165      * @type Boolean
26166      */
26167     hidden : false,
26168     /**
26169      * Read-only. True if this button is disabled
26170      * @type Boolean
26171      */
26172     disabled : false,
26173     /**
26174      * Read-only. True if this button is pressed (only if enableToggle = true)
26175      * @type Boolean
26176      */
26177     pressed : false,
26178
26179     /**
26180      * @cfg {Number} tabIndex 
26181      * The DOM tabIndex for this button (defaults to undefined)
26182      */
26183     tabIndex : undefined,
26184
26185     /**
26186      * @cfg {Boolean} enableToggle
26187      * True to enable pressed/not pressed toggling (defaults to false)
26188      */
26189     enableToggle: false,
26190     /**
26191      * @cfg {Mixed} menu
26192      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26193      */
26194     menu : undefined,
26195     /**
26196      * @cfg {String} menuAlign
26197      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26198      */
26199     menuAlign : "tl-bl?",
26200
26201     /**
26202      * @cfg {String} iconCls
26203      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26204      */
26205     iconCls : undefined,
26206     /**
26207      * @cfg {String} type
26208      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26209      */
26210     type : 'button',
26211
26212     // private
26213     menuClassTarget: 'tr',
26214
26215     /**
26216      * @cfg {String} clickEvent
26217      * The type of event to map to the button's event handler (defaults to 'click')
26218      */
26219     clickEvent : 'click',
26220
26221     /**
26222      * @cfg {Boolean} handleMouseEvents
26223      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26224      */
26225     handleMouseEvents : true,
26226
26227     /**
26228      * @cfg {String} tooltipType
26229      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26230      */
26231     tooltipType : 'qtip',
26232
26233     /**
26234      * @cfg {String} cls
26235      * A CSS class to apply to the button's main element.
26236      */
26237     
26238     /**
26239      * @cfg {Roo.Template} template (Optional)
26240      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26241      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26242      * require code modifications if required elements (e.g. a button) aren't present.
26243      */
26244
26245     // private
26246     render : function(renderTo){
26247         var btn;
26248         if(this.hideParent){
26249             this.parentEl = Roo.get(renderTo);
26250         }
26251         if(!this.dhconfig){
26252             if(!this.template){
26253                 if(!Roo.Button.buttonTemplate){
26254                     // hideous table template
26255                     Roo.Button.buttonTemplate = new Roo.Template(
26256                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26257                         '<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>',
26258                         "</tr></tbody></table>");
26259                 }
26260                 this.template = Roo.Button.buttonTemplate;
26261             }
26262             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26263             var btnEl = btn.child("button:first");
26264             btnEl.on('focus', this.onFocus, this);
26265             btnEl.on('blur', this.onBlur, this);
26266             if(this.cls){
26267                 btn.addClass(this.cls);
26268             }
26269             if(this.icon){
26270                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26271             }
26272             if(this.iconCls){
26273                 btnEl.addClass(this.iconCls);
26274                 if(!this.cls){
26275                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26276                 }
26277             }
26278             if(this.tabIndex !== undefined){
26279                 btnEl.dom.tabIndex = this.tabIndex;
26280             }
26281             if(this.tooltip){
26282                 if(typeof this.tooltip == 'object'){
26283                     Roo.QuickTips.tips(Roo.apply({
26284                           target: btnEl.id
26285                     }, this.tooltip));
26286                 } else {
26287                     btnEl.dom[this.tooltipType] = this.tooltip;
26288                 }
26289             }
26290         }else{
26291             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26292         }
26293         this.el = btn;
26294         if(this.id){
26295             this.el.dom.id = this.el.id = this.id;
26296         }
26297         if(this.menu){
26298             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26299             this.menu.on("show", this.onMenuShow, this);
26300             this.menu.on("hide", this.onMenuHide, this);
26301         }
26302         btn.addClass("x-btn");
26303         if(Roo.isIE && !Roo.isIE7){
26304             this.autoWidth.defer(1, this);
26305         }else{
26306             this.autoWidth();
26307         }
26308         if(this.handleMouseEvents){
26309             btn.on("mouseover", this.onMouseOver, this);
26310             btn.on("mouseout", this.onMouseOut, this);
26311             btn.on("mousedown", this.onMouseDown, this);
26312         }
26313         btn.on(this.clickEvent, this.onClick, this);
26314         //btn.on("mouseup", this.onMouseUp, this);
26315         if(this.hidden){
26316             this.hide();
26317         }
26318         if(this.disabled){
26319             this.disable();
26320         }
26321         Roo.ButtonToggleMgr.register(this);
26322         if(this.pressed){
26323             this.el.addClass("x-btn-pressed");
26324         }
26325         if(this.repeat){
26326             var repeater = new Roo.util.ClickRepeater(btn,
26327                 typeof this.repeat == "object" ? this.repeat : {}
26328             );
26329             repeater.on("click", this.onClick,  this);
26330         }
26331         
26332         this.fireEvent('render', this);
26333         
26334     },
26335     /**
26336      * Returns the button's underlying element
26337      * @return {Roo.Element} The element
26338      */
26339     getEl : function(){
26340         return this.el;  
26341     },
26342     
26343     /**
26344      * Destroys this Button and removes any listeners.
26345      */
26346     destroy : function(){
26347         Roo.ButtonToggleMgr.unregister(this);
26348         this.el.removeAllListeners();
26349         this.purgeListeners();
26350         this.el.remove();
26351     },
26352
26353     // private
26354     autoWidth : function(){
26355         if(this.el){
26356             this.el.setWidth("auto");
26357             if(Roo.isIE7 && Roo.isStrict){
26358                 var ib = this.el.child('button');
26359                 if(ib && ib.getWidth() > 20){
26360                     ib.clip();
26361                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26362                 }
26363             }
26364             if(this.minWidth){
26365                 if(this.hidden){
26366                     this.el.beginMeasure();
26367                 }
26368                 if(this.el.getWidth() < this.minWidth){
26369                     this.el.setWidth(this.minWidth);
26370                 }
26371                 if(this.hidden){
26372                     this.el.endMeasure();
26373                 }
26374             }
26375         }
26376     },
26377
26378     /**
26379      * Assigns this button's click handler
26380      * @param {Function} handler The function to call when the button is clicked
26381      * @param {Object} scope (optional) Scope for the function passed in
26382      */
26383     setHandler : function(handler, scope){
26384         this.handler = handler;
26385         this.scope = scope;  
26386     },
26387     
26388     /**
26389      * Sets this button's text
26390      * @param {String} text The button text
26391      */
26392     setText : function(text){
26393         this.text = text;
26394         if(this.el){
26395             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26396         }
26397         this.autoWidth();
26398     },
26399     
26400     /**
26401      * Gets the text for this button
26402      * @return {String} The button text
26403      */
26404     getText : function(){
26405         return this.text;  
26406     },
26407     
26408     /**
26409      * Show this button
26410      */
26411     show: function(){
26412         this.hidden = false;
26413         if(this.el){
26414             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26415         }
26416     },
26417     
26418     /**
26419      * Hide this button
26420      */
26421     hide: function(){
26422         this.hidden = true;
26423         if(this.el){
26424             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26425         }
26426     },
26427     
26428     /**
26429      * Convenience function for boolean show/hide
26430      * @param {Boolean} visible True to show, false to hide
26431      */
26432     setVisible: function(visible){
26433         if(visible) {
26434             this.show();
26435         }else{
26436             this.hide();
26437         }
26438     },
26439     
26440     /**
26441      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26442      * @param {Boolean} state (optional) Force a particular state
26443      */
26444     toggle : function(state){
26445         state = state === undefined ? !this.pressed : state;
26446         if(state != this.pressed){
26447             if(state){
26448                 this.el.addClass("x-btn-pressed");
26449                 this.pressed = true;
26450                 this.fireEvent("toggle", this, true);
26451             }else{
26452                 this.el.removeClass("x-btn-pressed");
26453                 this.pressed = false;
26454                 this.fireEvent("toggle", this, false);
26455             }
26456             if(this.toggleHandler){
26457                 this.toggleHandler.call(this.scope || this, this, state);
26458             }
26459         }
26460     },
26461     
26462     /**
26463      * Focus the button
26464      */
26465     focus : function(){
26466         this.el.child('button:first').focus();
26467     },
26468     
26469     /**
26470      * Disable this button
26471      */
26472     disable : function(){
26473         if(this.el){
26474             this.el.addClass("x-btn-disabled");
26475         }
26476         this.disabled = true;
26477     },
26478     
26479     /**
26480      * Enable this button
26481      */
26482     enable : function(){
26483         if(this.el){
26484             this.el.removeClass("x-btn-disabled");
26485         }
26486         this.disabled = false;
26487     },
26488
26489     /**
26490      * Convenience function for boolean enable/disable
26491      * @param {Boolean} enabled True to enable, false to disable
26492      */
26493     setDisabled : function(v){
26494         this[v !== true ? "enable" : "disable"]();
26495     },
26496
26497     // private
26498     onClick : function(e){
26499         if(e){
26500             e.preventDefault();
26501         }
26502         if(e.button != 0){
26503             return;
26504         }
26505         if(!this.disabled){
26506             if(this.enableToggle){
26507                 this.toggle();
26508             }
26509             if(this.menu && !this.menu.isVisible()){
26510                 this.menu.show(this.el, this.menuAlign);
26511             }
26512             this.fireEvent("click", this, e);
26513             if(this.handler){
26514                 this.el.removeClass("x-btn-over");
26515                 this.handler.call(this.scope || this, this, e);
26516             }
26517         }
26518     },
26519     // private
26520     onMouseOver : function(e){
26521         if(!this.disabled){
26522             this.el.addClass("x-btn-over");
26523             this.fireEvent('mouseover', this, e);
26524         }
26525     },
26526     // private
26527     onMouseOut : function(e){
26528         if(!e.within(this.el,  true)){
26529             this.el.removeClass("x-btn-over");
26530             this.fireEvent('mouseout', this, e);
26531         }
26532     },
26533     // private
26534     onFocus : function(e){
26535         if(!this.disabled){
26536             this.el.addClass("x-btn-focus");
26537         }
26538     },
26539     // private
26540     onBlur : function(e){
26541         this.el.removeClass("x-btn-focus");
26542     },
26543     // private
26544     onMouseDown : function(e){
26545         if(!this.disabled && e.button == 0){
26546             this.el.addClass("x-btn-click");
26547             Roo.get(document).on('mouseup', this.onMouseUp, this);
26548         }
26549     },
26550     // private
26551     onMouseUp : function(e){
26552         if(e.button == 0){
26553             this.el.removeClass("x-btn-click");
26554             Roo.get(document).un('mouseup', this.onMouseUp, this);
26555         }
26556     },
26557     // private
26558     onMenuShow : function(e){
26559         this.el.addClass("x-btn-menu-active");
26560     },
26561     // private
26562     onMenuHide : function(e){
26563         this.el.removeClass("x-btn-menu-active");
26564     }   
26565 });
26566
26567 // Private utility class used by Button
26568 Roo.ButtonToggleMgr = function(){
26569    var groups = {};
26570    
26571    function toggleGroup(btn, state){
26572        if(state){
26573            var g = groups[btn.toggleGroup];
26574            for(var i = 0, l = g.length; i < l; i++){
26575                if(g[i] != btn){
26576                    g[i].toggle(false);
26577                }
26578            }
26579        }
26580    }
26581    
26582    return {
26583        register : function(btn){
26584            if(!btn.toggleGroup){
26585                return;
26586            }
26587            var g = groups[btn.toggleGroup];
26588            if(!g){
26589                g = groups[btn.toggleGroup] = [];
26590            }
26591            g.push(btn);
26592            btn.on("toggle", toggleGroup);
26593        },
26594        
26595        unregister : function(btn){
26596            if(!btn.toggleGroup){
26597                return;
26598            }
26599            var g = groups[btn.toggleGroup];
26600            if(g){
26601                g.remove(btn);
26602                btn.un("toggle", toggleGroup);
26603            }
26604        }
26605    };
26606 }();/*
26607  * Based on:
26608  * Ext JS Library 1.1.1
26609  * Copyright(c) 2006-2007, Ext JS, LLC.
26610  *
26611  * Originally Released Under LGPL - original licence link has changed is not relivant.
26612  *
26613  * Fork - LGPL
26614  * <script type="text/javascript">
26615  */
26616  
26617 /**
26618  * @class Roo.SplitButton
26619  * @extends Roo.Button
26620  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26621  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26622  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26623  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26624  * @cfg {String} arrowTooltip The title attribute of the arrow
26625  * @constructor
26626  * Create a new menu button
26627  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26628  * @param {Object} config The config object
26629  */
26630 Roo.SplitButton = function(renderTo, config){
26631     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26632     /**
26633      * @event arrowclick
26634      * Fires when this button's arrow is clicked
26635      * @param {SplitButton} this
26636      * @param {EventObject} e The click event
26637      */
26638     this.addEvents({"arrowclick":true});
26639 };
26640
26641 Roo.extend(Roo.SplitButton, Roo.Button, {
26642     render : function(renderTo){
26643         // this is one sweet looking template!
26644         var tpl = new Roo.Template(
26645             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26646             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26647             '<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>',
26648             "</tbody></table></td><td>",
26649             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26650             '<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>',
26651             "</tbody></table></td></tr></table>"
26652         );
26653         var btn = tpl.append(renderTo, [this.text, this.type], true);
26654         var btnEl = btn.child("button");
26655         if(this.cls){
26656             btn.addClass(this.cls);
26657         }
26658         if(this.icon){
26659             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26660         }
26661         if(this.iconCls){
26662             btnEl.addClass(this.iconCls);
26663             if(!this.cls){
26664                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26665             }
26666         }
26667         this.el = btn;
26668         if(this.handleMouseEvents){
26669             btn.on("mouseover", this.onMouseOver, this);
26670             btn.on("mouseout", this.onMouseOut, this);
26671             btn.on("mousedown", this.onMouseDown, this);
26672             btn.on("mouseup", this.onMouseUp, this);
26673         }
26674         btn.on(this.clickEvent, this.onClick, this);
26675         if(this.tooltip){
26676             if(typeof this.tooltip == 'object'){
26677                 Roo.QuickTips.tips(Roo.apply({
26678                       target: btnEl.id
26679                 }, this.tooltip));
26680             } else {
26681                 btnEl.dom[this.tooltipType] = this.tooltip;
26682             }
26683         }
26684         if(this.arrowTooltip){
26685             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26686         }
26687         if(this.hidden){
26688             this.hide();
26689         }
26690         if(this.disabled){
26691             this.disable();
26692         }
26693         if(this.pressed){
26694             this.el.addClass("x-btn-pressed");
26695         }
26696         if(Roo.isIE && !Roo.isIE7){
26697             this.autoWidth.defer(1, this);
26698         }else{
26699             this.autoWidth();
26700         }
26701         if(this.menu){
26702             this.menu.on("show", this.onMenuShow, this);
26703             this.menu.on("hide", this.onMenuHide, this);
26704         }
26705         this.fireEvent('render', this);
26706     },
26707
26708     // private
26709     autoWidth : function(){
26710         if(this.el){
26711             var tbl = this.el.child("table:first");
26712             var tbl2 = this.el.child("table:last");
26713             this.el.setWidth("auto");
26714             tbl.setWidth("auto");
26715             if(Roo.isIE7 && Roo.isStrict){
26716                 var ib = this.el.child('button:first');
26717                 if(ib && ib.getWidth() > 20){
26718                     ib.clip();
26719                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26720                 }
26721             }
26722             if(this.minWidth){
26723                 if(this.hidden){
26724                     this.el.beginMeasure();
26725                 }
26726                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26727                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26728                 }
26729                 if(this.hidden){
26730                     this.el.endMeasure();
26731                 }
26732             }
26733             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26734         } 
26735     },
26736     /**
26737      * Sets this button's click handler
26738      * @param {Function} handler The function to call when the button is clicked
26739      * @param {Object} scope (optional) Scope for the function passed above
26740      */
26741     setHandler : function(handler, scope){
26742         this.handler = handler;
26743         this.scope = scope;  
26744     },
26745     
26746     /**
26747      * Sets this button's arrow click handler
26748      * @param {Function} handler The function to call when the arrow is clicked
26749      * @param {Object} scope (optional) Scope for the function passed above
26750      */
26751     setArrowHandler : function(handler, scope){
26752         this.arrowHandler = handler;
26753         this.scope = scope;  
26754     },
26755     
26756     /**
26757      * Focus the button
26758      */
26759     focus : function(){
26760         if(this.el){
26761             this.el.child("button:first").focus();
26762         }
26763     },
26764
26765     // private
26766     onClick : function(e){
26767         e.preventDefault();
26768         if(!this.disabled){
26769             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26770                 if(this.menu && !this.menu.isVisible()){
26771                     this.menu.show(this.el, this.menuAlign);
26772                 }
26773                 this.fireEvent("arrowclick", this, e);
26774                 if(this.arrowHandler){
26775                     this.arrowHandler.call(this.scope || this, this, e);
26776                 }
26777             }else{
26778                 this.fireEvent("click", this, e);
26779                 if(this.handler){
26780                     this.handler.call(this.scope || this, this, e);
26781                 }
26782             }
26783         }
26784     },
26785     // private
26786     onMouseDown : function(e){
26787         if(!this.disabled){
26788             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26789         }
26790     },
26791     // private
26792     onMouseUp : function(e){
26793         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26794     }   
26795 });
26796
26797
26798 // backwards compat
26799 Roo.MenuButton = Roo.SplitButton;/*
26800  * Based on:
26801  * Ext JS Library 1.1.1
26802  * Copyright(c) 2006-2007, Ext JS, LLC.
26803  *
26804  * Originally Released Under LGPL - original licence link has changed is not relivant.
26805  *
26806  * Fork - LGPL
26807  * <script type="text/javascript">
26808  */
26809
26810 /**
26811  * @class Roo.Toolbar
26812  * Basic Toolbar class.
26813  * @constructor
26814  * Creates a new Toolbar
26815  * @param {Object} container The config object
26816  */ 
26817 Roo.Toolbar = function(container, buttons, config)
26818 {
26819     /// old consturctor format still supported..
26820     if(container instanceof Array){ // omit the container for later rendering
26821         buttons = container;
26822         config = buttons;
26823         container = null;
26824     }
26825     if (typeof(container) == 'object' && container.xtype) {
26826         config = container;
26827         container = config.container;
26828         buttons = config.buttons || []; // not really - use items!!
26829     }
26830     var xitems = [];
26831     if (config && config.items) {
26832         xitems = config.items;
26833         delete config.items;
26834     }
26835     Roo.apply(this, config);
26836     this.buttons = buttons;
26837     
26838     if(container){
26839         this.render(container);
26840     }
26841     this.xitems = xitems;
26842     Roo.each(xitems, function(b) {
26843         this.add(b);
26844     }, this);
26845     
26846 };
26847
26848 Roo.Toolbar.prototype = {
26849     /**
26850      * @cfg {Array} items
26851      * array of button configs or elements to add (will be converted to a MixedCollection)
26852      */
26853     
26854     /**
26855      * @cfg {String/HTMLElement/Element} container
26856      * The id or element that will contain the toolbar
26857      */
26858     // private
26859     render : function(ct){
26860         this.el = Roo.get(ct);
26861         if(this.cls){
26862             this.el.addClass(this.cls);
26863         }
26864         // using a table allows for vertical alignment
26865         // 100% width is needed by Safari...
26866         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26867         this.tr = this.el.child("tr", true);
26868         var autoId = 0;
26869         this.items = new Roo.util.MixedCollection(false, function(o){
26870             return o.id || ("item" + (++autoId));
26871         });
26872         if(this.buttons){
26873             this.add.apply(this, this.buttons);
26874             delete this.buttons;
26875         }
26876     },
26877
26878     /**
26879      * Adds element(s) to the toolbar -- this function takes a variable number of 
26880      * arguments of mixed type and adds them to the toolbar.
26881      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26882      * <ul>
26883      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26884      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26885      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26886      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26887      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26888      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26889      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26890      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26891      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26892      * </ul>
26893      * @param {Mixed} arg2
26894      * @param {Mixed} etc.
26895      */
26896     add : function(){
26897         var a = arguments, l = a.length;
26898         for(var i = 0; i < l; i++){
26899             this._add(a[i]);
26900         }
26901     },
26902     // private..
26903     _add : function(el) {
26904         
26905         if (el.xtype) {
26906             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26907         }
26908         
26909         if (el.applyTo){ // some kind of form field
26910             return this.addField(el);
26911         } 
26912         if (el.render){ // some kind of Toolbar.Item
26913             return this.addItem(el);
26914         }
26915         if (typeof el == "string"){ // string
26916             if(el == "separator" || el == "-"){
26917                 return this.addSeparator();
26918             }
26919             if (el == " "){
26920                 return this.addSpacer();
26921             }
26922             if(el == "->"){
26923                 return this.addFill();
26924             }
26925             return this.addText(el);
26926             
26927         }
26928         if(el.tagName){ // element
26929             return this.addElement(el);
26930         }
26931         if(typeof el == "object"){ // must be button config?
26932             return this.addButton(el);
26933         }
26934         // and now what?!?!
26935         return false;
26936         
26937     },
26938     
26939     /**
26940      * Add an Xtype element
26941      * @param {Object} xtype Xtype Object
26942      * @return {Object} created Object
26943      */
26944     addxtype : function(e){
26945         return this.add(e);  
26946     },
26947     
26948     /**
26949      * Returns the Element for this toolbar.
26950      * @return {Roo.Element}
26951      */
26952     getEl : function(){
26953         return this.el;  
26954     },
26955     
26956     /**
26957      * Adds a separator
26958      * @return {Roo.Toolbar.Item} The separator item
26959      */
26960     addSeparator : function(){
26961         return this.addItem(new Roo.Toolbar.Separator());
26962     },
26963
26964     /**
26965      * Adds a spacer element
26966      * @return {Roo.Toolbar.Spacer} The spacer item
26967      */
26968     addSpacer : function(){
26969         return this.addItem(new Roo.Toolbar.Spacer());
26970     },
26971
26972     /**
26973      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26974      * @return {Roo.Toolbar.Fill} The fill item
26975      */
26976     addFill : function(){
26977         return this.addItem(new Roo.Toolbar.Fill());
26978     },
26979
26980     /**
26981      * Adds any standard HTML element to the toolbar
26982      * @param {String/HTMLElement/Element} el The element or id of the element to add
26983      * @return {Roo.Toolbar.Item} The element's item
26984      */
26985     addElement : function(el){
26986         return this.addItem(new Roo.Toolbar.Item(el));
26987     },
26988     /**
26989      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26990      * @type Roo.util.MixedCollection  
26991      */
26992     items : false,
26993      
26994     /**
26995      * Adds any Toolbar.Item or subclass
26996      * @param {Roo.Toolbar.Item} item
26997      * @return {Roo.Toolbar.Item} The item
26998      */
26999     addItem : function(item){
27000         var td = this.nextBlock();
27001         item.render(td);
27002         this.items.add(item);
27003         return item;
27004     },
27005     
27006     /**
27007      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27008      * @param {Object/Array} config A button config or array of configs
27009      * @return {Roo.Toolbar.Button/Array}
27010      */
27011     addButton : function(config){
27012         if(config instanceof Array){
27013             var buttons = [];
27014             for(var i = 0, len = config.length; i < len; i++) {
27015                 buttons.push(this.addButton(config[i]));
27016             }
27017             return buttons;
27018         }
27019         var b = config;
27020         if(!(config instanceof Roo.Toolbar.Button)){
27021             b = config.split ?
27022                 new Roo.Toolbar.SplitButton(config) :
27023                 new Roo.Toolbar.Button(config);
27024         }
27025         var td = this.nextBlock();
27026         b.render(td);
27027         this.items.add(b);
27028         return b;
27029     },
27030     
27031     /**
27032      * Adds text to the toolbar
27033      * @param {String} text The text to add
27034      * @return {Roo.Toolbar.Item} The element's item
27035      */
27036     addText : function(text){
27037         return this.addItem(new Roo.Toolbar.TextItem(text));
27038     },
27039     
27040     /**
27041      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27042      * @param {Number} index The index where the item is to be inserted
27043      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27044      * @return {Roo.Toolbar.Button/Item}
27045      */
27046     insertButton : function(index, item){
27047         if(item instanceof Array){
27048             var buttons = [];
27049             for(var i = 0, len = item.length; i < len; i++) {
27050                buttons.push(this.insertButton(index + i, item[i]));
27051             }
27052             return buttons;
27053         }
27054         if (!(item instanceof Roo.Toolbar.Button)){
27055            item = new Roo.Toolbar.Button(item);
27056         }
27057         var td = document.createElement("td");
27058         this.tr.insertBefore(td, this.tr.childNodes[index]);
27059         item.render(td);
27060         this.items.insert(index, item);
27061         return item;
27062     },
27063     
27064     /**
27065      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27066      * @param {Object} config
27067      * @return {Roo.Toolbar.Item} The element's item
27068      */
27069     addDom : function(config, returnEl){
27070         var td = this.nextBlock();
27071         Roo.DomHelper.overwrite(td, config);
27072         var ti = new Roo.Toolbar.Item(td.firstChild);
27073         ti.render(td);
27074         this.items.add(ti);
27075         return ti;
27076     },
27077
27078     /**
27079      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27080      * @type Roo.util.MixedCollection  
27081      */
27082     fields : false,
27083     
27084     /**
27085      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27086      * Note: the field should not have been rendered yet. For a field that has already been
27087      * rendered, use {@link #addElement}.
27088      * @param {Roo.form.Field} field
27089      * @return {Roo.ToolbarItem}
27090      */
27091      
27092       
27093     addField : function(field) {
27094         if (!this.fields) {
27095             var autoId = 0;
27096             this.fields = new Roo.util.MixedCollection(false, function(o){
27097                 return o.id || ("item" + (++autoId));
27098             });
27099
27100         }
27101         
27102         var td = this.nextBlock();
27103         field.render(td);
27104         var ti = new Roo.Toolbar.Item(td.firstChild);
27105         ti.render(td);
27106         this.items.add(ti);
27107         this.fields.add(field);
27108         return ti;
27109     },
27110     /**
27111      * Hide the toolbar
27112      * @method hide
27113      */
27114      
27115       
27116     hide : function()
27117     {
27118         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27119         this.el.child('div').hide();
27120     },
27121     /**
27122      * Show the toolbar
27123      * @method show
27124      */
27125     show : function()
27126     {
27127         this.el.child('div').show();
27128     },
27129       
27130     // private
27131     nextBlock : function(){
27132         var td = document.createElement("td");
27133         this.tr.appendChild(td);
27134         return td;
27135     },
27136
27137     // private
27138     destroy : function(){
27139         if(this.items){ // rendered?
27140             Roo.destroy.apply(Roo, this.items.items);
27141         }
27142         if(this.fields){ // rendered?
27143             Roo.destroy.apply(Roo, this.fields.items);
27144         }
27145         Roo.Element.uncache(this.el, this.tr);
27146     }
27147 };
27148
27149 /**
27150  * @class Roo.Toolbar.Item
27151  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27152  * @constructor
27153  * Creates a new Item
27154  * @param {HTMLElement} el 
27155  */
27156 Roo.Toolbar.Item = function(el){
27157     this.el = Roo.getDom(el);
27158     this.id = Roo.id(this.el);
27159     this.hidden = false;
27160 };
27161
27162 Roo.Toolbar.Item.prototype = {
27163     
27164     /**
27165      * Get this item's HTML Element
27166      * @return {HTMLElement}
27167      */
27168     getEl : function(){
27169        return this.el;  
27170     },
27171
27172     // private
27173     render : function(td){
27174         this.td = td;
27175         td.appendChild(this.el);
27176     },
27177     
27178     /**
27179      * Removes and destroys this item.
27180      */
27181     destroy : function(){
27182         this.td.parentNode.removeChild(this.td);
27183     },
27184     
27185     /**
27186      * Shows this item.
27187      */
27188     show: function(){
27189         this.hidden = false;
27190         this.td.style.display = "";
27191     },
27192     
27193     /**
27194      * Hides this item.
27195      */
27196     hide: function(){
27197         this.hidden = true;
27198         this.td.style.display = "none";
27199     },
27200     
27201     /**
27202      * Convenience function for boolean show/hide.
27203      * @param {Boolean} visible true to show/false to hide
27204      */
27205     setVisible: function(visible){
27206         if(visible) {
27207             this.show();
27208         }else{
27209             this.hide();
27210         }
27211     },
27212     
27213     /**
27214      * Try to focus this item.
27215      */
27216     focus : function(){
27217         Roo.fly(this.el).focus();
27218     },
27219     
27220     /**
27221      * Disables this item.
27222      */
27223     disable : function(){
27224         Roo.fly(this.td).addClass("x-item-disabled");
27225         this.disabled = true;
27226         this.el.disabled = true;
27227     },
27228     
27229     /**
27230      * Enables this item.
27231      */
27232     enable : function(){
27233         Roo.fly(this.td).removeClass("x-item-disabled");
27234         this.disabled = false;
27235         this.el.disabled = false;
27236     }
27237 };
27238
27239
27240 /**
27241  * @class Roo.Toolbar.Separator
27242  * @extends Roo.Toolbar.Item
27243  * A simple toolbar separator class
27244  * @constructor
27245  * Creates a new Separator
27246  */
27247 Roo.Toolbar.Separator = function(){
27248     var s = document.createElement("span");
27249     s.className = "ytb-sep";
27250     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27251 };
27252 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27253     enable:Roo.emptyFn,
27254     disable:Roo.emptyFn,
27255     focus:Roo.emptyFn
27256 });
27257
27258 /**
27259  * @class Roo.Toolbar.Spacer
27260  * @extends Roo.Toolbar.Item
27261  * A simple element that adds extra horizontal space to a toolbar.
27262  * @constructor
27263  * Creates a new Spacer
27264  */
27265 Roo.Toolbar.Spacer = function(){
27266     var s = document.createElement("div");
27267     s.className = "ytb-spacer";
27268     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27269 };
27270 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27271     enable:Roo.emptyFn,
27272     disable:Roo.emptyFn,
27273     focus:Roo.emptyFn
27274 });
27275
27276 /**
27277  * @class Roo.Toolbar.Fill
27278  * @extends Roo.Toolbar.Spacer
27279  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27280  * @constructor
27281  * Creates a new Spacer
27282  */
27283 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27284     // private
27285     render : function(td){
27286         td.style.width = '100%';
27287         Roo.Toolbar.Fill.superclass.render.call(this, td);
27288     }
27289 });
27290
27291 /**
27292  * @class Roo.Toolbar.TextItem
27293  * @extends Roo.Toolbar.Item
27294  * A simple class that renders text directly into a toolbar.
27295  * @constructor
27296  * Creates a new TextItem
27297  * @param {String} text
27298  */
27299 Roo.Toolbar.TextItem = function(text){
27300     if (typeof(text) == 'object') {
27301         text = text.text;
27302     }
27303     var s = document.createElement("span");
27304     s.className = "ytb-text";
27305     s.innerHTML = text;
27306     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27307 };
27308 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27309     enable:Roo.emptyFn,
27310     disable:Roo.emptyFn,
27311     focus:Roo.emptyFn
27312 });
27313
27314 /**
27315  * @class Roo.Toolbar.Button
27316  * @extends Roo.Button
27317  * A button that renders into a toolbar.
27318  * @constructor
27319  * Creates a new Button
27320  * @param {Object} config A standard {@link Roo.Button} config object
27321  */
27322 Roo.Toolbar.Button = function(config){
27323     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27324 };
27325 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27326     render : function(td){
27327         this.td = td;
27328         Roo.Toolbar.Button.superclass.render.call(this, td);
27329     },
27330     
27331     /**
27332      * Removes and destroys this button
27333      */
27334     destroy : function(){
27335         Roo.Toolbar.Button.superclass.destroy.call(this);
27336         this.td.parentNode.removeChild(this.td);
27337     },
27338     
27339     /**
27340      * Shows this button
27341      */
27342     show: function(){
27343         this.hidden = false;
27344         this.td.style.display = "";
27345     },
27346     
27347     /**
27348      * Hides this button
27349      */
27350     hide: function(){
27351         this.hidden = true;
27352         this.td.style.display = "none";
27353     },
27354
27355     /**
27356      * Disables this item
27357      */
27358     disable : function(){
27359         Roo.fly(this.td).addClass("x-item-disabled");
27360         this.disabled = true;
27361     },
27362
27363     /**
27364      * Enables this item
27365      */
27366     enable : function(){
27367         Roo.fly(this.td).removeClass("x-item-disabled");
27368         this.disabled = false;
27369     }
27370 });
27371 // backwards compat
27372 Roo.ToolbarButton = Roo.Toolbar.Button;
27373
27374 /**
27375  * @class Roo.Toolbar.SplitButton
27376  * @extends Roo.SplitButton
27377  * A menu button that renders into a toolbar.
27378  * @constructor
27379  * Creates a new SplitButton
27380  * @param {Object} config A standard {@link Roo.SplitButton} config object
27381  */
27382 Roo.Toolbar.SplitButton = function(config){
27383     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27384 };
27385 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27386     render : function(td){
27387         this.td = td;
27388         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27389     },
27390     
27391     /**
27392      * Removes and destroys this button
27393      */
27394     destroy : function(){
27395         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27396         this.td.parentNode.removeChild(this.td);
27397     },
27398     
27399     /**
27400      * Shows this button
27401      */
27402     show: function(){
27403         this.hidden = false;
27404         this.td.style.display = "";
27405     },
27406     
27407     /**
27408      * Hides this button
27409      */
27410     hide: function(){
27411         this.hidden = true;
27412         this.td.style.display = "none";
27413     }
27414 });
27415
27416 // backwards compat
27417 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27418  * Based on:
27419  * Ext JS Library 1.1.1
27420  * Copyright(c) 2006-2007, Ext JS, LLC.
27421  *
27422  * Originally Released Under LGPL - original licence link has changed is not relivant.
27423  *
27424  * Fork - LGPL
27425  * <script type="text/javascript">
27426  */
27427  
27428 /**
27429  * @class Roo.PagingToolbar
27430  * @extends Roo.Toolbar
27431  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27432  * @constructor
27433  * Create a new PagingToolbar
27434  * @param {Object} config The config object
27435  */
27436 Roo.PagingToolbar = function(el, ds, config)
27437 {
27438     // old args format still supported... - xtype is prefered..
27439     if (typeof(el) == 'object' && el.xtype) {
27440         // created from xtype...
27441         config = el;
27442         ds = el.dataSource;
27443         el = config.container;
27444     }
27445     var items = [];
27446     if (config.items) {
27447         items = config.items;
27448         config.items = [];
27449     }
27450     
27451     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27452     this.ds = ds;
27453     this.cursor = 0;
27454     this.renderButtons(this.el);
27455     this.bind(ds);
27456     
27457     // supprot items array.
27458    
27459     Roo.each(items, function(e) {
27460         this.add(Roo.factory(e));
27461     },this);
27462     
27463 };
27464
27465 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27466     /**
27467      * @cfg {Roo.data.Store} dataSource
27468      * The underlying data store providing the paged data
27469      */
27470     /**
27471      * @cfg {String/HTMLElement/Element} container
27472      * container The id or element that will contain the toolbar
27473      */
27474     /**
27475      * @cfg {Boolean} displayInfo
27476      * True to display the displayMsg (defaults to false)
27477      */
27478     /**
27479      * @cfg {Number} pageSize
27480      * The number of records to display per page (defaults to 20)
27481      */
27482     pageSize: 20,
27483     /**
27484      * @cfg {String} displayMsg
27485      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27486      */
27487     displayMsg : 'Displaying {0} - {1} of {2}',
27488     /**
27489      * @cfg {String} emptyMsg
27490      * The message to display when no records are found (defaults to "No data to display")
27491      */
27492     emptyMsg : 'No data to display',
27493     /**
27494      * Customizable piece of the default paging text (defaults to "Page")
27495      * @type String
27496      */
27497     beforePageText : "Page",
27498     /**
27499      * Customizable piece of the default paging text (defaults to "of %0")
27500      * @type String
27501      */
27502     afterPageText : "of {0}",
27503     /**
27504      * Customizable piece of the default paging text (defaults to "First Page")
27505      * @type String
27506      */
27507     firstText : "First Page",
27508     /**
27509      * Customizable piece of the default paging text (defaults to "Previous Page")
27510      * @type String
27511      */
27512     prevText : "Previous Page",
27513     /**
27514      * Customizable piece of the default paging text (defaults to "Next Page")
27515      * @type String
27516      */
27517     nextText : "Next Page",
27518     /**
27519      * Customizable piece of the default paging text (defaults to "Last Page")
27520      * @type String
27521      */
27522     lastText : "Last Page",
27523     /**
27524      * Customizable piece of the default paging text (defaults to "Refresh")
27525      * @type String
27526      */
27527     refreshText : "Refresh",
27528
27529     // private
27530     renderButtons : function(el){
27531         Roo.PagingToolbar.superclass.render.call(this, el);
27532         this.first = this.addButton({
27533             tooltip: this.firstText,
27534             cls: "x-btn-icon x-grid-page-first",
27535             disabled: true,
27536             handler: this.onClick.createDelegate(this, ["first"])
27537         });
27538         this.prev = this.addButton({
27539             tooltip: this.prevText,
27540             cls: "x-btn-icon x-grid-page-prev",
27541             disabled: true,
27542             handler: this.onClick.createDelegate(this, ["prev"])
27543         });
27544         //this.addSeparator();
27545         this.add(this.beforePageText);
27546         this.field = Roo.get(this.addDom({
27547            tag: "input",
27548            type: "text",
27549            size: "3",
27550            value: "1",
27551            cls: "x-grid-page-number"
27552         }).el);
27553         this.field.on("keydown", this.onPagingKeydown, this);
27554         this.field.on("focus", function(){this.dom.select();});
27555         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27556         this.field.setHeight(18);
27557         //this.addSeparator();
27558         this.next = this.addButton({
27559             tooltip: this.nextText,
27560             cls: "x-btn-icon x-grid-page-next",
27561             disabled: true,
27562             handler: this.onClick.createDelegate(this, ["next"])
27563         });
27564         this.last = this.addButton({
27565             tooltip: this.lastText,
27566             cls: "x-btn-icon x-grid-page-last",
27567             disabled: true,
27568             handler: this.onClick.createDelegate(this, ["last"])
27569         });
27570         //this.addSeparator();
27571         this.loading = this.addButton({
27572             tooltip: this.refreshText,
27573             cls: "x-btn-icon x-grid-loading",
27574             handler: this.onClick.createDelegate(this, ["refresh"])
27575         });
27576
27577         if(this.displayInfo){
27578             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27579         }
27580     },
27581
27582     // private
27583     updateInfo : function(){
27584         if(this.displayEl){
27585             var count = this.ds.getCount();
27586             var msg = count == 0 ?
27587                 this.emptyMsg :
27588                 String.format(
27589                     this.displayMsg,
27590                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27591                 );
27592             this.displayEl.update(msg);
27593         }
27594     },
27595
27596     // private
27597     onLoad : function(ds, r, o){
27598        this.cursor = o.params ? o.params.start : 0;
27599        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27600
27601        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27602        this.field.dom.value = ap;
27603        this.first.setDisabled(ap == 1);
27604        this.prev.setDisabled(ap == 1);
27605        this.next.setDisabled(ap == ps);
27606        this.last.setDisabled(ap == ps);
27607        this.loading.enable();
27608        this.updateInfo();
27609     },
27610
27611     // private
27612     getPageData : function(){
27613         var total = this.ds.getTotalCount();
27614         return {
27615             total : total,
27616             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27617             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27618         };
27619     },
27620
27621     // private
27622     onLoadError : function(){
27623         this.loading.enable();
27624     },
27625
27626     // private
27627     onPagingKeydown : function(e){
27628         var k = e.getKey();
27629         var d = this.getPageData();
27630         if(k == e.RETURN){
27631             var v = this.field.dom.value, pageNum;
27632             if(!v || isNaN(pageNum = parseInt(v, 10))){
27633                 this.field.dom.value = d.activePage;
27634                 return;
27635             }
27636             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27637             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27638             e.stopEvent();
27639         }
27640         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))
27641         {
27642           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27643           this.field.dom.value = pageNum;
27644           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27645           e.stopEvent();
27646         }
27647         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27648         {
27649           var v = this.field.dom.value, pageNum; 
27650           var increment = (e.shiftKey) ? 10 : 1;
27651           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27652             increment *= -1;
27653           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27654             this.field.dom.value = d.activePage;
27655             return;
27656           }
27657           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27658           {
27659             this.field.dom.value = parseInt(v, 10) + increment;
27660             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27661             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27662           }
27663           e.stopEvent();
27664         }
27665     },
27666
27667     // private
27668     beforeLoad : function(){
27669         if(this.loading){
27670             this.loading.disable();
27671         }
27672     },
27673
27674     // private
27675     onClick : function(which){
27676         var ds = this.ds;
27677         switch(which){
27678             case "first":
27679                 ds.load({params:{start: 0, limit: this.pageSize}});
27680             break;
27681             case "prev":
27682                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27683             break;
27684             case "next":
27685                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27686             break;
27687             case "last":
27688                 var total = ds.getTotalCount();
27689                 var extra = total % this.pageSize;
27690                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27691                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27692             break;
27693             case "refresh":
27694                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27695             break;
27696         }
27697     },
27698
27699     /**
27700      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27701      * @param {Roo.data.Store} store The data store to unbind
27702      */
27703     unbind : function(ds){
27704         ds.un("beforeload", this.beforeLoad, this);
27705         ds.un("load", this.onLoad, this);
27706         ds.un("loadexception", this.onLoadError, this);
27707         ds.un("remove", this.updateInfo, this);
27708         ds.un("add", this.updateInfo, this);
27709         this.ds = undefined;
27710     },
27711
27712     /**
27713      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27714      * @param {Roo.data.Store} store The data store to bind
27715      */
27716     bind : function(ds){
27717         ds.on("beforeload", this.beforeLoad, this);
27718         ds.on("load", this.onLoad, this);
27719         ds.on("loadexception", this.onLoadError, this);
27720         ds.on("remove", this.updateInfo, this);
27721         ds.on("add", this.updateInfo, this);
27722         this.ds = ds;
27723     }
27724 });/*
27725  * Based on:
27726  * Ext JS Library 1.1.1
27727  * Copyright(c) 2006-2007, Ext JS, LLC.
27728  *
27729  * Originally Released Under LGPL - original licence link has changed is not relivant.
27730  *
27731  * Fork - LGPL
27732  * <script type="text/javascript">
27733  */
27734
27735 /**
27736  * @class Roo.Resizable
27737  * @extends Roo.util.Observable
27738  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27739  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27740  * 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
27741  * the element will be wrapped for you automatically.</p>
27742  * <p>Here is the list of valid resize handles:</p>
27743  * <pre>
27744 Value   Description
27745 ------  -------------------
27746  'n'     north
27747  's'     south
27748  'e'     east
27749  'w'     west
27750  'nw'    northwest
27751  'sw'    southwest
27752  'se'    southeast
27753  'ne'    northeast
27754  'hd'    horizontal drag
27755  'all'   all
27756 </pre>
27757  * <p>Here's an example showing the creation of a typical Resizable:</p>
27758  * <pre><code>
27759 var resizer = new Roo.Resizable("element-id", {
27760     handles: 'all',
27761     minWidth: 200,
27762     minHeight: 100,
27763     maxWidth: 500,
27764     maxHeight: 400,
27765     pinned: true
27766 });
27767 resizer.on("resize", myHandler);
27768 </code></pre>
27769  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27770  * resizer.east.setDisplayed(false);</p>
27771  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27772  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27773  * resize operation's new size (defaults to [0, 0])
27774  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27775  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27776  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27777  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27778  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27779  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27780  * @cfg {Number} width The width of the element in pixels (defaults to null)
27781  * @cfg {Number} height The height of the element in pixels (defaults to null)
27782  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27783  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27784  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27785  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27786  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27787  * in favor of the handles config option (defaults to false)
27788  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27789  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27790  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27791  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27792  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27793  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27794  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27795  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27796  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27797  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27798  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27799  * @constructor
27800  * Create a new resizable component
27801  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27802  * @param {Object} config configuration options
27803   */
27804 Roo.Resizable = function(el, config)
27805 {
27806     this.el = Roo.get(el);
27807
27808     if(config && config.wrap){
27809         config.resizeChild = this.el;
27810         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27811         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27812         this.el.setStyle("overflow", "hidden");
27813         this.el.setPositioning(config.resizeChild.getPositioning());
27814         config.resizeChild.clearPositioning();
27815         if(!config.width || !config.height){
27816             var csize = config.resizeChild.getSize();
27817             this.el.setSize(csize.width, csize.height);
27818         }
27819         if(config.pinned && !config.adjustments){
27820             config.adjustments = "auto";
27821         }
27822     }
27823
27824     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27825     this.proxy.unselectable();
27826     this.proxy.enableDisplayMode('block');
27827
27828     Roo.apply(this, config);
27829
27830     if(this.pinned){
27831         this.disableTrackOver = true;
27832         this.el.addClass("x-resizable-pinned");
27833     }
27834     // if the element isn't positioned, make it relative
27835     var position = this.el.getStyle("position");
27836     if(position != "absolute" && position != "fixed"){
27837         this.el.setStyle("position", "relative");
27838     }
27839     if(!this.handles){ // no handles passed, must be legacy style
27840         this.handles = 's,e,se';
27841         if(this.multiDirectional){
27842             this.handles += ',n,w';
27843         }
27844     }
27845     if(this.handles == "all"){
27846         this.handles = "n s e w ne nw se sw";
27847     }
27848     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27849     var ps = Roo.Resizable.positions;
27850     for(var i = 0, len = hs.length; i < len; i++){
27851         if(hs[i] && ps[hs[i]]){
27852             var pos = ps[hs[i]];
27853             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27854         }
27855     }
27856     // legacy
27857     this.corner = this.southeast;
27858     
27859     // updateBox = the box can move..
27860     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27861         this.updateBox = true;
27862     }
27863
27864     this.activeHandle = null;
27865
27866     if(this.resizeChild){
27867         if(typeof this.resizeChild == "boolean"){
27868             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27869         }else{
27870             this.resizeChild = Roo.get(this.resizeChild, true);
27871         }
27872     }
27873     
27874     if(this.adjustments == "auto"){
27875         var rc = this.resizeChild;
27876         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27877         if(rc && (hw || hn)){
27878             rc.position("relative");
27879             rc.setLeft(hw ? hw.el.getWidth() : 0);
27880             rc.setTop(hn ? hn.el.getHeight() : 0);
27881         }
27882         this.adjustments = [
27883             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27884             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27885         ];
27886     }
27887
27888     if(this.draggable){
27889         this.dd = this.dynamic ?
27890             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27891         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27892     }
27893
27894     // public events
27895     this.addEvents({
27896         /**
27897          * @event beforeresize
27898          * Fired before resize is allowed. Set enabled to false to cancel resize.
27899          * @param {Roo.Resizable} this
27900          * @param {Roo.EventObject} e The mousedown event
27901          */
27902         "beforeresize" : true,
27903         /**
27904          * @event resize
27905          * Fired after a resize.
27906          * @param {Roo.Resizable} this
27907          * @param {Number} width The new width
27908          * @param {Number} height The new height
27909          * @param {Roo.EventObject} e The mouseup event
27910          */
27911         "resize" : true
27912     });
27913
27914     if(this.width !== null && this.height !== null){
27915         this.resizeTo(this.width, this.height);
27916     }else{
27917         this.updateChildSize();
27918     }
27919     if(Roo.isIE){
27920         this.el.dom.style.zoom = 1;
27921     }
27922     Roo.Resizable.superclass.constructor.call(this);
27923 };
27924
27925 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27926         resizeChild : false,
27927         adjustments : [0, 0],
27928         minWidth : 5,
27929         minHeight : 5,
27930         maxWidth : 10000,
27931         maxHeight : 10000,
27932         enabled : true,
27933         animate : false,
27934         duration : .35,
27935         dynamic : false,
27936         handles : false,
27937         multiDirectional : false,
27938         disableTrackOver : false,
27939         easing : 'easeOutStrong',
27940         widthIncrement : 0,
27941         heightIncrement : 0,
27942         pinned : false,
27943         width : null,
27944         height : null,
27945         preserveRatio : false,
27946         transparent: false,
27947         minX: 0,
27948         minY: 0,
27949         draggable: false,
27950
27951         /**
27952          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27953          */
27954         constrainTo: undefined,
27955         /**
27956          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27957          */
27958         resizeRegion: undefined,
27959
27960
27961     /**
27962      * Perform a manual resize
27963      * @param {Number} width
27964      * @param {Number} height
27965      */
27966     resizeTo : function(width, height){
27967         this.el.setSize(width, height);
27968         this.updateChildSize();
27969         this.fireEvent("resize", this, width, height, null);
27970     },
27971
27972     // private
27973     startSizing : function(e, handle){
27974         this.fireEvent("beforeresize", this, e);
27975         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27976
27977             if(!this.overlay){
27978                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27979                 this.overlay.unselectable();
27980                 this.overlay.enableDisplayMode("block");
27981                 this.overlay.on("mousemove", this.onMouseMove, this);
27982                 this.overlay.on("mouseup", this.onMouseUp, this);
27983             }
27984             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27985
27986             this.resizing = true;
27987             this.startBox = this.el.getBox();
27988             this.startPoint = e.getXY();
27989             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27990                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27991
27992             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27993             this.overlay.show();
27994
27995             if(this.constrainTo) {
27996                 var ct = Roo.get(this.constrainTo);
27997                 this.resizeRegion = ct.getRegion().adjust(
27998                     ct.getFrameWidth('t'),
27999                     ct.getFrameWidth('l'),
28000                     -ct.getFrameWidth('b'),
28001                     -ct.getFrameWidth('r')
28002                 );
28003             }
28004
28005             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28006             this.proxy.show();
28007             this.proxy.setBox(this.startBox);
28008             if(!this.dynamic){
28009                 this.proxy.setStyle('visibility', 'visible');
28010             }
28011         }
28012     },
28013
28014     // private
28015     onMouseDown : function(handle, e){
28016         if(this.enabled){
28017             e.stopEvent();
28018             this.activeHandle = handle;
28019             this.startSizing(e, handle);
28020         }
28021     },
28022
28023     // private
28024     onMouseUp : function(e){
28025         var size = this.resizeElement();
28026         this.resizing = false;
28027         this.handleOut();
28028         this.overlay.hide();
28029         this.proxy.hide();
28030         this.fireEvent("resize", this, size.width, size.height, e);
28031     },
28032
28033     // private
28034     updateChildSize : function(){
28035         if(this.resizeChild){
28036             var el = this.el;
28037             var child = this.resizeChild;
28038             var adj = this.adjustments;
28039             if(el.dom.offsetWidth){
28040                 var b = el.getSize(true);
28041                 child.setSize(b.width+adj[0], b.height+adj[1]);
28042             }
28043             // Second call here for IE
28044             // The first call enables instant resizing and
28045             // the second call corrects scroll bars if they
28046             // exist
28047             if(Roo.isIE){
28048                 setTimeout(function(){
28049                     if(el.dom.offsetWidth){
28050                         var b = el.getSize(true);
28051                         child.setSize(b.width+adj[0], b.height+adj[1]);
28052                     }
28053                 }, 10);
28054             }
28055         }
28056     },
28057
28058     // private
28059     snap : function(value, inc, min){
28060         if(!inc || !value) return value;
28061         var newValue = value;
28062         var m = value % inc;
28063         if(m > 0){
28064             if(m > (inc/2)){
28065                 newValue = value + (inc-m);
28066             }else{
28067                 newValue = value - m;
28068             }
28069         }
28070         return Math.max(min, newValue);
28071     },
28072
28073     // private
28074     resizeElement : function(){
28075         var box = this.proxy.getBox();
28076         if(this.updateBox){
28077             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28078         }else{
28079             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28080         }
28081         this.updateChildSize();
28082         if(!this.dynamic){
28083             this.proxy.hide();
28084         }
28085         return box;
28086     },
28087
28088     // private
28089     constrain : function(v, diff, m, mx){
28090         if(v - diff < m){
28091             diff = v - m;
28092         }else if(v - diff > mx){
28093             diff = mx - v;
28094         }
28095         return diff;
28096     },
28097
28098     // private
28099     onMouseMove : function(e){
28100         if(this.enabled){
28101             try{// try catch so if something goes wrong the user doesn't get hung
28102
28103             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28104                 return;
28105             }
28106
28107             //var curXY = this.startPoint;
28108             var curSize = this.curSize || this.startBox;
28109             var x = this.startBox.x, y = this.startBox.y;
28110             var ox = x, oy = y;
28111             var w = curSize.width, h = curSize.height;
28112             var ow = w, oh = h;
28113             var mw = this.minWidth, mh = this.minHeight;
28114             var mxw = this.maxWidth, mxh = this.maxHeight;
28115             var wi = this.widthIncrement;
28116             var hi = this.heightIncrement;
28117
28118             var eventXY = e.getXY();
28119             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28120             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28121
28122             var pos = this.activeHandle.position;
28123
28124             switch(pos){
28125                 case "east":
28126                     w += diffX;
28127                     w = Math.min(Math.max(mw, w), mxw);
28128                     break;
28129              
28130                 case "south":
28131                     h += diffY;
28132                     h = Math.min(Math.max(mh, h), mxh);
28133                     break;
28134                 case "southeast":
28135                     w += diffX;
28136                     h += diffY;
28137                     w = Math.min(Math.max(mw, w), mxw);
28138                     h = Math.min(Math.max(mh, h), mxh);
28139                     break;
28140                 case "north":
28141                     diffY = this.constrain(h, diffY, mh, mxh);
28142                     y += diffY;
28143                     h -= diffY;
28144                     break;
28145                 case "hdrag":
28146                     
28147                     if (wi) {
28148                         var adiffX = Math.abs(diffX);
28149                         var sub = (adiffX % wi); // how much 
28150                         if (sub > (wi/2)) { // far enough to snap
28151                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28152                         } else {
28153                             // remove difference.. 
28154                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28155                         }
28156                     }
28157                     x += diffX;
28158                     x = Math.max(this.minX, x);
28159                     break;
28160                 case "west":
28161                     diffX = this.constrain(w, diffX, mw, mxw);
28162                     x += diffX;
28163                     w -= diffX;
28164                     break;
28165                 case "northeast":
28166                     w += diffX;
28167                     w = Math.min(Math.max(mw, w), mxw);
28168                     diffY = this.constrain(h, diffY, mh, mxh);
28169                     y += diffY;
28170                     h -= diffY;
28171                     break;
28172                 case "northwest":
28173                     diffX = this.constrain(w, diffX, mw, mxw);
28174                     diffY = this.constrain(h, diffY, mh, mxh);
28175                     y += diffY;
28176                     h -= diffY;
28177                     x += diffX;
28178                     w -= diffX;
28179                     break;
28180                case "southwest":
28181                     diffX = this.constrain(w, diffX, mw, mxw);
28182                     h += diffY;
28183                     h = Math.min(Math.max(mh, h), mxh);
28184                     x += diffX;
28185                     w -= diffX;
28186                     break;
28187             }
28188
28189             var sw = this.snap(w, wi, mw);
28190             var sh = this.snap(h, hi, mh);
28191             if(sw != w || sh != h){
28192                 switch(pos){
28193                     case "northeast":
28194                         y -= sh - h;
28195                     break;
28196                     case "north":
28197                         y -= sh - h;
28198                         break;
28199                     case "southwest":
28200                         x -= sw - w;
28201                     break;
28202                     case "west":
28203                         x -= sw - w;
28204                         break;
28205                     case "northwest":
28206                         x -= sw - w;
28207                         y -= sh - h;
28208                     break;
28209                 }
28210                 w = sw;
28211                 h = sh;
28212             }
28213
28214             if(this.preserveRatio){
28215                 switch(pos){
28216                     case "southeast":
28217                     case "east":
28218                         h = oh * (w/ow);
28219                         h = Math.min(Math.max(mh, h), mxh);
28220                         w = ow * (h/oh);
28221                        break;
28222                     case "south":
28223                         w = ow * (h/oh);
28224                         w = Math.min(Math.max(mw, w), mxw);
28225                         h = oh * (w/ow);
28226                         break;
28227                     case "northeast":
28228                         w = ow * (h/oh);
28229                         w = Math.min(Math.max(mw, w), mxw);
28230                         h = oh * (w/ow);
28231                     break;
28232                     case "north":
28233                         var tw = w;
28234                         w = ow * (h/oh);
28235                         w = Math.min(Math.max(mw, w), mxw);
28236                         h = oh * (w/ow);
28237                         x += (tw - w) / 2;
28238                         break;
28239                     case "southwest":
28240                         h = oh * (w/ow);
28241                         h = Math.min(Math.max(mh, h), mxh);
28242                         var tw = w;
28243                         w = ow * (h/oh);
28244                         x += tw - w;
28245                         break;
28246                     case "west":
28247                         var th = h;
28248                         h = oh * (w/ow);
28249                         h = Math.min(Math.max(mh, h), mxh);
28250                         y += (th - h) / 2;
28251                         var tw = w;
28252                         w = ow * (h/oh);
28253                         x += tw - w;
28254                        break;
28255                     case "northwest":
28256                         var tw = w;
28257                         var th = h;
28258                         h = oh * (w/ow);
28259                         h = Math.min(Math.max(mh, h), mxh);
28260                         w = ow * (h/oh);
28261                         y += th - h;
28262                         x += tw - w;
28263                        break;
28264
28265                 }
28266             }
28267             if (pos == 'hdrag') {
28268                 w = ow;
28269             }
28270             this.proxy.setBounds(x, y, w, h);
28271             if(this.dynamic){
28272                 this.resizeElement();
28273             }
28274             }catch(e){}
28275         }
28276     },
28277
28278     // private
28279     handleOver : function(){
28280         if(this.enabled){
28281             this.el.addClass("x-resizable-over");
28282         }
28283     },
28284
28285     // private
28286     handleOut : function(){
28287         if(!this.resizing){
28288             this.el.removeClass("x-resizable-over");
28289         }
28290     },
28291
28292     /**
28293      * Returns the element this component is bound to.
28294      * @return {Roo.Element}
28295      */
28296     getEl : function(){
28297         return this.el;
28298     },
28299
28300     /**
28301      * Returns the resizeChild element (or null).
28302      * @return {Roo.Element}
28303      */
28304     getResizeChild : function(){
28305         return this.resizeChild;
28306     },
28307
28308     /**
28309      * Destroys this resizable. If the element was wrapped and
28310      * removeEl is not true then the element remains.
28311      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28312      */
28313     destroy : function(removeEl){
28314         this.proxy.remove();
28315         if(this.overlay){
28316             this.overlay.removeAllListeners();
28317             this.overlay.remove();
28318         }
28319         var ps = Roo.Resizable.positions;
28320         for(var k in ps){
28321             if(typeof ps[k] != "function" && this[ps[k]]){
28322                 var h = this[ps[k]];
28323                 h.el.removeAllListeners();
28324                 h.el.remove();
28325             }
28326         }
28327         if(removeEl){
28328             this.el.update("");
28329             this.el.remove();
28330         }
28331     }
28332 });
28333
28334 // private
28335 // hash to map config positions to true positions
28336 Roo.Resizable.positions = {
28337     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28338     hd: "hdrag"
28339 };
28340
28341 // private
28342 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28343     if(!this.tpl){
28344         // only initialize the template if resizable is used
28345         var tpl = Roo.DomHelper.createTemplate(
28346             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28347         );
28348         tpl.compile();
28349         Roo.Resizable.Handle.prototype.tpl = tpl;
28350     }
28351     this.position = pos;
28352     this.rz = rz;
28353     // show north drag fro topdra
28354     var handlepos = pos == 'hdrag' ? 'north' : pos;
28355     
28356     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28357     if (pos == 'hdrag') {
28358         this.el.setStyle('cursor', 'pointer');
28359     }
28360     this.el.unselectable();
28361     if(transparent){
28362         this.el.setOpacity(0);
28363     }
28364     this.el.on("mousedown", this.onMouseDown, this);
28365     if(!disableTrackOver){
28366         this.el.on("mouseover", this.onMouseOver, this);
28367         this.el.on("mouseout", this.onMouseOut, this);
28368     }
28369 };
28370
28371 // private
28372 Roo.Resizable.Handle.prototype = {
28373     afterResize : function(rz){
28374         // do nothing
28375     },
28376     // private
28377     onMouseDown : function(e){
28378         this.rz.onMouseDown(this, e);
28379     },
28380     // private
28381     onMouseOver : function(e){
28382         this.rz.handleOver(this, e);
28383     },
28384     // private
28385     onMouseOut : function(e){
28386         this.rz.handleOut(this, e);
28387     }
28388 };/*
28389  * Based on:
28390  * Ext JS Library 1.1.1
28391  * Copyright(c) 2006-2007, Ext JS, LLC.
28392  *
28393  * Originally Released Under LGPL - original licence link has changed is not relivant.
28394  *
28395  * Fork - LGPL
28396  * <script type="text/javascript">
28397  */
28398
28399 /**
28400  * @class Roo.Editor
28401  * @extends Roo.Component
28402  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28403  * @constructor
28404  * Create a new Editor
28405  * @param {Roo.form.Field} field The Field object (or descendant)
28406  * @param {Object} config The config object
28407  */
28408 Roo.Editor = function(field, config){
28409     Roo.Editor.superclass.constructor.call(this, config);
28410     this.field = field;
28411     this.addEvents({
28412         /**
28413              * @event beforestartedit
28414              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28415              * false from the handler of this event.
28416              * @param {Editor} this
28417              * @param {Roo.Element} boundEl The underlying element bound to this editor
28418              * @param {Mixed} value The field value being set
28419              */
28420         "beforestartedit" : true,
28421         /**
28422              * @event startedit
28423              * Fires when this editor is displayed
28424              * @param {Roo.Element} boundEl The underlying element bound to this editor
28425              * @param {Mixed} value The starting field value
28426              */
28427         "startedit" : true,
28428         /**
28429              * @event beforecomplete
28430              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28431              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28432              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28433              * event will not fire since no edit actually occurred.
28434              * @param {Editor} this
28435              * @param {Mixed} value The current field value
28436              * @param {Mixed} startValue The original field value
28437              */
28438         "beforecomplete" : true,
28439         /**
28440              * @event complete
28441              * Fires after editing is complete and any changed value has been written to the underlying field.
28442              * @param {Editor} this
28443              * @param {Mixed} value The current field value
28444              * @param {Mixed} startValue The original field value
28445              */
28446         "complete" : true,
28447         /**
28448          * @event specialkey
28449          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28450          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28451          * @param {Roo.form.Field} this
28452          * @param {Roo.EventObject} e The event object
28453          */
28454         "specialkey" : true
28455     });
28456 };
28457
28458 Roo.extend(Roo.Editor, Roo.Component, {
28459     /**
28460      * @cfg {Boolean/String} autosize
28461      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28462      * or "height" to adopt the height only (defaults to false)
28463      */
28464     /**
28465      * @cfg {Boolean} revertInvalid
28466      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28467      * validation fails (defaults to true)
28468      */
28469     /**
28470      * @cfg {Boolean} ignoreNoChange
28471      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28472      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28473      * will never be ignored.
28474      */
28475     /**
28476      * @cfg {Boolean} hideEl
28477      * False to keep the bound element visible while the editor is displayed (defaults to true)
28478      */
28479     /**
28480      * @cfg {Mixed} value
28481      * The data value of the underlying field (defaults to "")
28482      */
28483     value : "",
28484     /**
28485      * @cfg {String} alignment
28486      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28487      */
28488     alignment: "c-c?",
28489     /**
28490      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28491      * for bottom-right shadow (defaults to "frame")
28492      */
28493     shadow : "frame",
28494     /**
28495      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28496      */
28497     constrain : false,
28498     /**
28499      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28500      */
28501     completeOnEnter : false,
28502     /**
28503      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28504      */
28505     cancelOnEsc : false,
28506     /**
28507      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28508      */
28509     updateEl : false,
28510
28511     // private
28512     onRender : function(ct, position){
28513         this.el = new Roo.Layer({
28514             shadow: this.shadow,
28515             cls: "x-editor",
28516             parentEl : ct,
28517             shim : this.shim,
28518             shadowOffset:4,
28519             id: this.id,
28520             constrain: this.constrain
28521         });
28522         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28523         if(this.field.msgTarget != 'title'){
28524             this.field.msgTarget = 'qtip';
28525         }
28526         this.field.render(this.el);
28527         if(Roo.isGecko){
28528             this.field.el.dom.setAttribute('autocomplete', 'off');
28529         }
28530         this.field.on("specialkey", this.onSpecialKey, this);
28531         if(this.swallowKeys){
28532             this.field.el.swallowEvent(['keydown','keypress']);
28533         }
28534         this.field.show();
28535         this.field.on("blur", this.onBlur, this);
28536         if(this.field.grow){
28537             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28538         }
28539     },
28540
28541     onSpecialKey : function(field, e)
28542     {
28543         //Roo.log('editor onSpecialKey');
28544         if(this.completeOnEnter && e.getKey() == e.ENTER){
28545             e.stopEvent();
28546             this.completeEdit();
28547             return;
28548         }
28549         // do not fire special key otherwise it might hide close the editor...
28550         if(e.getKey() == e.ENTER){    
28551             return;
28552         }
28553         if(this.cancelOnEsc && e.getKey() == e.ESC){
28554             this.cancelEdit();
28555             return;
28556         } 
28557         this.fireEvent('specialkey', field, e);
28558     
28559     },
28560
28561     /**
28562      * Starts the editing process and shows the editor.
28563      * @param {String/HTMLElement/Element} el The element to edit
28564      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28565       * to the innerHTML of el.
28566      */
28567     startEdit : function(el, value){
28568         if(this.editing){
28569             this.completeEdit();
28570         }
28571         this.boundEl = Roo.get(el);
28572         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28573         if(!this.rendered){
28574             this.render(this.parentEl || document.body);
28575         }
28576         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28577             return;
28578         }
28579         this.startValue = v;
28580         this.field.setValue(v);
28581         if(this.autoSize){
28582             var sz = this.boundEl.getSize();
28583             switch(this.autoSize){
28584                 case "width":
28585                 this.setSize(sz.width,  "");
28586                 break;
28587                 case "height":
28588                 this.setSize("",  sz.height);
28589                 break;
28590                 default:
28591                 this.setSize(sz.width,  sz.height);
28592             }
28593         }
28594         this.el.alignTo(this.boundEl, this.alignment);
28595         this.editing = true;
28596         if(Roo.QuickTips){
28597             Roo.QuickTips.disable();
28598         }
28599         this.show();
28600     },
28601
28602     /**
28603      * Sets the height and width of this editor.
28604      * @param {Number} width The new width
28605      * @param {Number} height The new height
28606      */
28607     setSize : function(w, h){
28608         this.field.setSize(w, h);
28609         if(this.el){
28610             this.el.sync();
28611         }
28612     },
28613
28614     /**
28615      * Realigns the editor to the bound field based on the current alignment config value.
28616      */
28617     realign : function(){
28618         this.el.alignTo(this.boundEl, this.alignment);
28619     },
28620
28621     /**
28622      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28623      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28624      */
28625     completeEdit : function(remainVisible){
28626         if(!this.editing){
28627             return;
28628         }
28629         var v = this.getValue();
28630         if(this.revertInvalid !== false && !this.field.isValid()){
28631             v = this.startValue;
28632             this.cancelEdit(true);
28633         }
28634         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28635             this.editing = false;
28636             this.hide();
28637             return;
28638         }
28639         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28640             this.editing = false;
28641             if(this.updateEl && this.boundEl){
28642                 this.boundEl.update(v);
28643             }
28644             if(remainVisible !== true){
28645                 this.hide();
28646             }
28647             this.fireEvent("complete", this, v, this.startValue);
28648         }
28649     },
28650
28651     // private
28652     onShow : function(){
28653         this.el.show();
28654         if(this.hideEl !== false){
28655             this.boundEl.hide();
28656         }
28657         this.field.show();
28658         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28659             this.fixIEFocus = true;
28660             this.deferredFocus.defer(50, this);
28661         }else{
28662             this.field.focus();
28663         }
28664         this.fireEvent("startedit", this.boundEl, this.startValue);
28665     },
28666
28667     deferredFocus : function(){
28668         if(this.editing){
28669             this.field.focus();
28670         }
28671     },
28672
28673     /**
28674      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28675      * reverted to the original starting value.
28676      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28677      * cancel (defaults to false)
28678      */
28679     cancelEdit : function(remainVisible){
28680         if(this.editing){
28681             this.setValue(this.startValue);
28682             if(remainVisible !== true){
28683                 this.hide();
28684             }
28685         }
28686     },
28687
28688     // private
28689     onBlur : function(){
28690         if(this.allowBlur !== true && this.editing){
28691             this.completeEdit();
28692         }
28693     },
28694
28695     // private
28696     onHide : function(){
28697         if(this.editing){
28698             this.completeEdit();
28699             return;
28700         }
28701         this.field.blur();
28702         if(this.field.collapse){
28703             this.field.collapse();
28704         }
28705         this.el.hide();
28706         if(this.hideEl !== false){
28707             this.boundEl.show();
28708         }
28709         if(Roo.QuickTips){
28710             Roo.QuickTips.enable();
28711         }
28712     },
28713
28714     /**
28715      * Sets the data value of the editor
28716      * @param {Mixed} value Any valid value supported by the underlying field
28717      */
28718     setValue : function(v){
28719         this.field.setValue(v);
28720     },
28721
28722     /**
28723      * Gets the data value of the editor
28724      * @return {Mixed} The data value
28725      */
28726     getValue : function(){
28727         return this.field.getValue();
28728     }
28729 });/*
28730  * Based on:
28731  * Ext JS Library 1.1.1
28732  * Copyright(c) 2006-2007, Ext JS, LLC.
28733  *
28734  * Originally Released Under LGPL - original licence link has changed is not relivant.
28735  *
28736  * Fork - LGPL
28737  * <script type="text/javascript">
28738  */
28739  
28740 /**
28741  * @class Roo.BasicDialog
28742  * @extends Roo.util.Observable
28743  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28744  * <pre><code>
28745 var dlg = new Roo.BasicDialog("my-dlg", {
28746     height: 200,
28747     width: 300,
28748     minHeight: 100,
28749     minWidth: 150,
28750     modal: true,
28751     proxyDrag: true,
28752     shadow: true
28753 });
28754 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28755 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28756 dlg.addButton('Cancel', dlg.hide, dlg);
28757 dlg.show();
28758 </code></pre>
28759   <b>A Dialog should always be a direct child of the body element.</b>
28760  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28761  * @cfg {String} title Default text to display in the title bar (defaults to null)
28762  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28763  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28764  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28765  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28766  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28767  * (defaults to null with no animation)
28768  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28769  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28770  * property for valid values (defaults to 'all')
28771  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28772  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28773  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28774  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28775  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28776  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28777  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28778  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28779  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28780  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28781  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28782  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28783  * draggable = true (defaults to false)
28784  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28785  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28786  * shadow (defaults to false)
28787  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28788  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28789  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28790  * @cfg {Array} buttons Array of buttons
28791  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28792  * @constructor
28793  * Create a new BasicDialog.
28794  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28795  * @param {Object} config Configuration options
28796  */
28797 Roo.BasicDialog = function(el, config){
28798     this.el = Roo.get(el);
28799     var dh = Roo.DomHelper;
28800     if(!this.el && config && config.autoCreate){
28801         if(typeof config.autoCreate == "object"){
28802             if(!config.autoCreate.id){
28803                 config.autoCreate.id = el;
28804             }
28805             this.el = dh.append(document.body,
28806                         config.autoCreate, true);
28807         }else{
28808             this.el = dh.append(document.body,
28809                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28810         }
28811     }
28812     el = this.el;
28813     el.setDisplayed(true);
28814     el.hide = this.hideAction;
28815     this.id = el.id;
28816     el.addClass("x-dlg");
28817
28818     Roo.apply(this, config);
28819
28820     this.proxy = el.createProxy("x-dlg-proxy");
28821     this.proxy.hide = this.hideAction;
28822     this.proxy.setOpacity(.5);
28823     this.proxy.hide();
28824
28825     if(config.width){
28826         el.setWidth(config.width);
28827     }
28828     if(config.height){
28829         el.setHeight(config.height);
28830     }
28831     this.size = el.getSize();
28832     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28833         this.xy = [config.x,config.y];
28834     }else{
28835         this.xy = el.getCenterXY(true);
28836     }
28837     /** The header element @type Roo.Element */
28838     this.header = el.child("> .x-dlg-hd");
28839     /** The body element @type Roo.Element */
28840     this.body = el.child("> .x-dlg-bd");
28841     /** The footer element @type Roo.Element */
28842     this.footer = el.child("> .x-dlg-ft");
28843
28844     if(!this.header){
28845         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28846     }
28847     if(!this.body){
28848         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28849     }
28850
28851     this.header.unselectable();
28852     if(this.title){
28853         this.header.update(this.title);
28854     }
28855     // this element allows the dialog to be focused for keyboard event
28856     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28857     this.focusEl.swallowEvent("click", true);
28858
28859     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28860
28861     // wrap the body and footer for special rendering
28862     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28863     if(this.footer){
28864         this.bwrap.dom.appendChild(this.footer.dom);
28865     }
28866
28867     this.bg = this.el.createChild({
28868         tag: "div", cls:"x-dlg-bg",
28869         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28870     });
28871     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28872
28873
28874     if(this.autoScroll !== false && !this.autoTabs){
28875         this.body.setStyle("overflow", "auto");
28876     }
28877
28878     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28879
28880     if(this.closable !== false){
28881         this.el.addClass("x-dlg-closable");
28882         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28883         this.close.on("click", this.closeClick, this);
28884         this.close.addClassOnOver("x-dlg-close-over");
28885     }
28886     if(this.collapsible !== false){
28887         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28888         this.collapseBtn.on("click", this.collapseClick, this);
28889         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28890         this.header.on("dblclick", this.collapseClick, this);
28891     }
28892     if(this.resizable !== false){
28893         this.el.addClass("x-dlg-resizable");
28894         this.resizer = new Roo.Resizable(el, {
28895             minWidth: this.minWidth || 80,
28896             minHeight:this.minHeight || 80,
28897             handles: this.resizeHandles || "all",
28898             pinned: true
28899         });
28900         this.resizer.on("beforeresize", this.beforeResize, this);
28901         this.resizer.on("resize", this.onResize, this);
28902     }
28903     if(this.draggable !== false){
28904         el.addClass("x-dlg-draggable");
28905         if (!this.proxyDrag) {
28906             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28907         }
28908         else {
28909             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28910         }
28911         dd.setHandleElId(this.header.id);
28912         dd.endDrag = this.endMove.createDelegate(this);
28913         dd.startDrag = this.startMove.createDelegate(this);
28914         dd.onDrag = this.onDrag.createDelegate(this);
28915         dd.scroll = false;
28916         this.dd = dd;
28917     }
28918     if(this.modal){
28919         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28920         this.mask.enableDisplayMode("block");
28921         this.mask.hide();
28922         this.el.addClass("x-dlg-modal");
28923     }
28924     if(this.shadow){
28925         this.shadow = new Roo.Shadow({
28926             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28927             offset : this.shadowOffset
28928         });
28929     }else{
28930         this.shadowOffset = 0;
28931     }
28932     if(Roo.useShims && this.shim !== false){
28933         this.shim = this.el.createShim();
28934         this.shim.hide = this.hideAction;
28935         this.shim.hide();
28936     }else{
28937         this.shim = false;
28938     }
28939     if(this.autoTabs){
28940         this.initTabs();
28941     }
28942     if (this.buttons) { 
28943         var bts= this.buttons;
28944         this.buttons = [];
28945         Roo.each(bts, function(b) {
28946             this.addButton(b);
28947         }, this);
28948     }
28949     
28950     
28951     this.addEvents({
28952         /**
28953          * @event keydown
28954          * Fires when a key is pressed
28955          * @param {Roo.BasicDialog} this
28956          * @param {Roo.EventObject} e
28957          */
28958         "keydown" : true,
28959         /**
28960          * @event move
28961          * Fires when this dialog is moved by the user.
28962          * @param {Roo.BasicDialog} this
28963          * @param {Number} x The new page X
28964          * @param {Number} y The new page Y
28965          */
28966         "move" : true,
28967         /**
28968          * @event resize
28969          * Fires when this dialog is resized by the user.
28970          * @param {Roo.BasicDialog} this
28971          * @param {Number} width The new width
28972          * @param {Number} height The new height
28973          */
28974         "resize" : true,
28975         /**
28976          * @event beforehide
28977          * Fires before this dialog is hidden.
28978          * @param {Roo.BasicDialog} this
28979          */
28980         "beforehide" : true,
28981         /**
28982          * @event hide
28983          * Fires when this dialog is hidden.
28984          * @param {Roo.BasicDialog} this
28985          */
28986         "hide" : true,
28987         /**
28988          * @event beforeshow
28989          * Fires before this dialog is shown.
28990          * @param {Roo.BasicDialog} this
28991          */
28992         "beforeshow" : true,
28993         /**
28994          * @event show
28995          * Fires when this dialog is shown.
28996          * @param {Roo.BasicDialog} this
28997          */
28998         "show" : true
28999     });
29000     el.on("keydown", this.onKeyDown, this);
29001     el.on("mousedown", this.toFront, this);
29002     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29003     this.el.hide();
29004     Roo.DialogManager.register(this);
29005     Roo.BasicDialog.superclass.constructor.call(this);
29006 };
29007
29008 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29009     shadowOffset: Roo.isIE ? 6 : 5,
29010     minHeight: 80,
29011     minWidth: 200,
29012     minButtonWidth: 75,
29013     defaultButton: null,
29014     buttonAlign: "right",
29015     tabTag: 'div',
29016     firstShow: true,
29017
29018     /**
29019      * Sets the dialog title text
29020      * @param {String} text The title text to display
29021      * @return {Roo.BasicDialog} this
29022      */
29023     setTitle : function(text){
29024         this.header.update(text);
29025         return this;
29026     },
29027
29028     // private
29029     closeClick : function(){
29030         this.hide();
29031     },
29032
29033     // private
29034     collapseClick : function(){
29035         this[this.collapsed ? "expand" : "collapse"]();
29036     },
29037
29038     /**
29039      * Collapses the dialog to its minimized state (only the title bar is visible).
29040      * Equivalent to the user clicking the collapse dialog button.
29041      */
29042     collapse : function(){
29043         if(!this.collapsed){
29044             this.collapsed = true;
29045             this.el.addClass("x-dlg-collapsed");
29046             this.restoreHeight = this.el.getHeight();
29047             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29048         }
29049     },
29050
29051     /**
29052      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29053      * clicking the expand dialog button.
29054      */
29055     expand : function(){
29056         if(this.collapsed){
29057             this.collapsed = false;
29058             this.el.removeClass("x-dlg-collapsed");
29059             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29060         }
29061     },
29062
29063     /**
29064      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29065      * @return {Roo.TabPanel} The tabs component
29066      */
29067     initTabs : function(){
29068         var tabs = this.getTabs();
29069         while(tabs.getTab(0)){
29070             tabs.removeTab(0);
29071         }
29072         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29073             var dom = el.dom;
29074             tabs.addTab(Roo.id(dom), dom.title);
29075             dom.title = "";
29076         });
29077         tabs.activate(0);
29078         return tabs;
29079     },
29080
29081     // private
29082     beforeResize : function(){
29083         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29084     },
29085
29086     // private
29087     onResize : function(){
29088         this.refreshSize();
29089         this.syncBodyHeight();
29090         this.adjustAssets();
29091         this.focus();
29092         this.fireEvent("resize", this, this.size.width, this.size.height);
29093     },
29094
29095     // private
29096     onKeyDown : function(e){
29097         if(this.isVisible()){
29098             this.fireEvent("keydown", this, e);
29099         }
29100     },
29101
29102     /**
29103      * Resizes the dialog.
29104      * @param {Number} width
29105      * @param {Number} height
29106      * @return {Roo.BasicDialog} this
29107      */
29108     resizeTo : function(width, height){
29109         this.el.setSize(width, height);
29110         this.size = {width: width, height: height};
29111         this.syncBodyHeight();
29112         if(this.fixedcenter){
29113             this.center();
29114         }
29115         if(this.isVisible()){
29116             this.constrainXY();
29117             this.adjustAssets();
29118         }
29119         this.fireEvent("resize", this, width, height);
29120         return this;
29121     },
29122
29123
29124     /**
29125      * Resizes the dialog to fit the specified content size.
29126      * @param {Number} width
29127      * @param {Number} height
29128      * @return {Roo.BasicDialog} this
29129      */
29130     setContentSize : function(w, h){
29131         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29132         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29133         //if(!this.el.isBorderBox()){
29134             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29135             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29136         //}
29137         if(this.tabs){
29138             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29139             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29140         }
29141         this.resizeTo(w, h);
29142         return this;
29143     },
29144
29145     /**
29146      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29147      * executed in response to a particular key being pressed while the dialog is active.
29148      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29149      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29150      * @param {Function} fn The function to call
29151      * @param {Object} scope (optional) The scope of the function
29152      * @return {Roo.BasicDialog} this
29153      */
29154     addKeyListener : function(key, fn, scope){
29155         var keyCode, shift, ctrl, alt;
29156         if(typeof key == "object" && !(key instanceof Array)){
29157             keyCode = key["key"];
29158             shift = key["shift"];
29159             ctrl = key["ctrl"];
29160             alt = key["alt"];
29161         }else{
29162             keyCode = key;
29163         }
29164         var handler = function(dlg, e){
29165             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29166                 var k = e.getKey();
29167                 if(keyCode instanceof Array){
29168                     for(var i = 0, len = keyCode.length; i < len; i++){
29169                         if(keyCode[i] == k){
29170                           fn.call(scope || window, dlg, k, e);
29171                           return;
29172                         }
29173                     }
29174                 }else{
29175                     if(k == keyCode){
29176                         fn.call(scope || window, dlg, k, e);
29177                     }
29178                 }
29179             }
29180         };
29181         this.on("keydown", handler);
29182         return this;
29183     },
29184
29185     /**
29186      * Returns the TabPanel component (creates it if it doesn't exist).
29187      * Note: If you wish to simply check for the existence of tabs without creating them,
29188      * check for a null 'tabs' property.
29189      * @return {Roo.TabPanel} The tabs component
29190      */
29191     getTabs : function(){
29192         if(!this.tabs){
29193             this.el.addClass("x-dlg-auto-tabs");
29194             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29195             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29196         }
29197         return this.tabs;
29198     },
29199
29200     /**
29201      * Adds a button to the footer section of the dialog.
29202      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29203      * object or a valid Roo.DomHelper element config
29204      * @param {Function} handler The function called when the button is clicked
29205      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29206      * @return {Roo.Button} The new button
29207      */
29208     addButton : function(config, handler, scope){
29209         var dh = Roo.DomHelper;
29210         if(!this.footer){
29211             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29212         }
29213         if(!this.btnContainer){
29214             var tb = this.footer.createChild({
29215
29216                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29217                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29218             }, null, true);
29219             this.btnContainer = tb.firstChild.firstChild.firstChild;
29220         }
29221         var bconfig = {
29222             handler: handler,
29223             scope: scope,
29224             minWidth: this.minButtonWidth,
29225             hideParent:true
29226         };
29227         if(typeof config == "string"){
29228             bconfig.text = config;
29229         }else{
29230             if(config.tag){
29231                 bconfig.dhconfig = config;
29232             }else{
29233                 Roo.apply(bconfig, config);
29234             }
29235         }
29236         var fc = false;
29237         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29238             bconfig.position = Math.max(0, bconfig.position);
29239             fc = this.btnContainer.childNodes[bconfig.position];
29240         }
29241          
29242         var btn = new Roo.Button(
29243             fc ? 
29244                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29245                 : this.btnContainer.appendChild(document.createElement("td")),
29246             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29247             bconfig
29248         );
29249         this.syncBodyHeight();
29250         if(!this.buttons){
29251             /**
29252              * Array of all the buttons that have been added to this dialog via addButton
29253              * @type Array
29254              */
29255             this.buttons = [];
29256         }
29257         this.buttons.push(btn);
29258         return btn;
29259     },
29260
29261     /**
29262      * Sets the default button to be focused when the dialog is displayed.
29263      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29264      * @return {Roo.BasicDialog} this
29265      */
29266     setDefaultButton : function(btn){
29267         this.defaultButton = btn;
29268         return this;
29269     },
29270
29271     // private
29272     getHeaderFooterHeight : function(safe){
29273         var height = 0;
29274         if(this.header){
29275            height += this.header.getHeight();
29276         }
29277         if(this.footer){
29278            var fm = this.footer.getMargins();
29279             height += (this.footer.getHeight()+fm.top+fm.bottom);
29280         }
29281         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29282         height += this.centerBg.getPadding("tb");
29283         return height;
29284     },
29285
29286     // private
29287     syncBodyHeight : function(){
29288         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29289         var height = this.size.height - this.getHeaderFooterHeight(false);
29290         bd.setHeight(height-bd.getMargins("tb"));
29291         var hh = this.header.getHeight();
29292         var h = this.size.height-hh;
29293         cb.setHeight(h);
29294         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29295         bw.setHeight(h-cb.getPadding("tb"));
29296         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29297         bd.setWidth(bw.getWidth(true));
29298         if(this.tabs){
29299             this.tabs.syncHeight();
29300             if(Roo.isIE){
29301                 this.tabs.el.repaint();
29302             }
29303         }
29304     },
29305
29306     /**
29307      * Restores the previous state of the dialog if Roo.state is configured.
29308      * @return {Roo.BasicDialog} this
29309      */
29310     restoreState : function(){
29311         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29312         if(box && box.width){
29313             this.xy = [box.x, box.y];
29314             this.resizeTo(box.width, box.height);
29315         }
29316         return this;
29317     },
29318
29319     // private
29320     beforeShow : function(){
29321         this.expand();
29322         if(this.fixedcenter){
29323             this.xy = this.el.getCenterXY(true);
29324         }
29325         if(this.modal){
29326             Roo.get(document.body).addClass("x-body-masked");
29327             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29328             this.mask.show();
29329         }
29330         this.constrainXY();
29331     },
29332
29333     // private
29334     animShow : function(){
29335         var b = Roo.get(this.animateTarget).getBox();
29336         this.proxy.setSize(b.width, b.height);
29337         this.proxy.setLocation(b.x, b.y);
29338         this.proxy.show();
29339         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29340                     true, .35, this.showEl.createDelegate(this));
29341     },
29342
29343     /**
29344      * Shows the dialog.
29345      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29346      * @return {Roo.BasicDialog} this
29347      */
29348     show : function(animateTarget){
29349         if (this.fireEvent("beforeshow", this) === false){
29350             return;
29351         }
29352         if(this.syncHeightBeforeShow){
29353             this.syncBodyHeight();
29354         }else if(this.firstShow){
29355             this.firstShow = false;
29356             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29357         }
29358         this.animateTarget = animateTarget || this.animateTarget;
29359         if(!this.el.isVisible()){
29360             this.beforeShow();
29361             if(this.animateTarget && Roo.get(this.animateTarget)){
29362                 this.animShow();
29363             }else{
29364                 this.showEl();
29365             }
29366         }
29367         return this;
29368     },
29369
29370     // private
29371     showEl : function(){
29372         this.proxy.hide();
29373         this.el.setXY(this.xy);
29374         this.el.show();
29375         this.adjustAssets(true);
29376         this.toFront();
29377         this.focus();
29378         // IE peekaboo bug - fix found by Dave Fenwick
29379         if(Roo.isIE){
29380             this.el.repaint();
29381         }
29382         this.fireEvent("show", this);
29383     },
29384
29385     /**
29386      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29387      * dialog itself will receive focus.
29388      */
29389     focus : function(){
29390         if(this.defaultButton){
29391             this.defaultButton.focus();
29392         }else{
29393             this.focusEl.focus();
29394         }
29395     },
29396
29397     // private
29398     constrainXY : function(){
29399         if(this.constraintoviewport !== false){
29400             if(!this.viewSize){
29401                 if(this.container){
29402                     var s = this.container.getSize();
29403                     this.viewSize = [s.width, s.height];
29404                 }else{
29405                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29406                 }
29407             }
29408             var s = Roo.get(this.container||document).getScroll();
29409
29410             var x = this.xy[0], y = this.xy[1];
29411             var w = this.size.width, h = this.size.height;
29412             var vw = this.viewSize[0], vh = this.viewSize[1];
29413             // only move it if it needs it
29414             var moved = false;
29415             // first validate right/bottom
29416             if(x + w > vw+s.left){
29417                 x = vw - w;
29418                 moved = true;
29419             }
29420             if(y + h > vh+s.top){
29421                 y = vh - h;
29422                 moved = true;
29423             }
29424             // then make sure top/left isn't negative
29425             if(x < s.left){
29426                 x = s.left;
29427                 moved = true;
29428             }
29429             if(y < s.top){
29430                 y = s.top;
29431                 moved = true;
29432             }
29433             if(moved){
29434                 // cache xy
29435                 this.xy = [x, y];
29436                 if(this.isVisible()){
29437                     this.el.setLocation(x, y);
29438                     this.adjustAssets();
29439                 }
29440             }
29441         }
29442     },
29443
29444     // private
29445     onDrag : function(){
29446         if(!this.proxyDrag){
29447             this.xy = this.el.getXY();
29448             this.adjustAssets();
29449         }
29450     },
29451
29452     // private
29453     adjustAssets : function(doShow){
29454         var x = this.xy[0], y = this.xy[1];
29455         var w = this.size.width, h = this.size.height;
29456         if(doShow === true){
29457             if(this.shadow){
29458                 this.shadow.show(this.el);
29459             }
29460             if(this.shim){
29461                 this.shim.show();
29462             }
29463         }
29464         if(this.shadow && this.shadow.isVisible()){
29465             this.shadow.show(this.el);
29466         }
29467         if(this.shim && this.shim.isVisible()){
29468             this.shim.setBounds(x, y, w, h);
29469         }
29470     },
29471
29472     // private
29473     adjustViewport : function(w, h){
29474         if(!w || !h){
29475             w = Roo.lib.Dom.getViewWidth();
29476             h = Roo.lib.Dom.getViewHeight();
29477         }
29478         // cache the size
29479         this.viewSize = [w, h];
29480         if(this.modal && this.mask.isVisible()){
29481             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29482             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29483         }
29484         if(this.isVisible()){
29485             this.constrainXY();
29486         }
29487     },
29488
29489     /**
29490      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29491      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29492      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29493      */
29494     destroy : function(removeEl){
29495         if(this.isVisible()){
29496             this.animateTarget = null;
29497             this.hide();
29498         }
29499         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29500         if(this.tabs){
29501             this.tabs.destroy(removeEl);
29502         }
29503         Roo.destroy(
29504              this.shim,
29505              this.proxy,
29506              this.resizer,
29507              this.close,
29508              this.mask
29509         );
29510         if(this.dd){
29511             this.dd.unreg();
29512         }
29513         if(this.buttons){
29514            for(var i = 0, len = this.buttons.length; i < len; i++){
29515                this.buttons[i].destroy();
29516            }
29517         }
29518         this.el.removeAllListeners();
29519         if(removeEl === true){
29520             this.el.update("");
29521             this.el.remove();
29522         }
29523         Roo.DialogManager.unregister(this);
29524     },
29525
29526     // private
29527     startMove : function(){
29528         if(this.proxyDrag){
29529             this.proxy.show();
29530         }
29531         if(this.constraintoviewport !== false){
29532             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29533         }
29534     },
29535
29536     // private
29537     endMove : function(){
29538         if(!this.proxyDrag){
29539             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29540         }else{
29541             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29542             this.proxy.hide();
29543         }
29544         this.refreshSize();
29545         this.adjustAssets();
29546         this.focus();
29547         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29548     },
29549
29550     /**
29551      * Brings this dialog to the front of any other visible dialogs
29552      * @return {Roo.BasicDialog} this
29553      */
29554     toFront : function(){
29555         Roo.DialogManager.bringToFront(this);
29556         return this;
29557     },
29558
29559     /**
29560      * Sends this dialog to the back (under) of any other visible dialogs
29561      * @return {Roo.BasicDialog} this
29562      */
29563     toBack : function(){
29564         Roo.DialogManager.sendToBack(this);
29565         return this;
29566     },
29567
29568     /**
29569      * Centers this dialog in the viewport
29570      * @return {Roo.BasicDialog} this
29571      */
29572     center : function(){
29573         var xy = this.el.getCenterXY(true);
29574         this.moveTo(xy[0], xy[1]);
29575         return this;
29576     },
29577
29578     /**
29579      * Moves the dialog's top-left corner to the specified point
29580      * @param {Number} x
29581      * @param {Number} y
29582      * @return {Roo.BasicDialog} this
29583      */
29584     moveTo : function(x, y){
29585         this.xy = [x,y];
29586         if(this.isVisible()){
29587             this.el.setXY(this.xy);
29588             this.adjustAssets();
29589         }
29590         return this;
29591     },
29592
29593     /**
29594      * Aligns the dialog to the specified element
29595      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29596      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29597      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29598      * @return {Roo.BasicDialog} this
29599      */
29600     alignTo : function(element, position, offsets){
29601         this.xy = this.el.getAlignToXY(element, position, offsets);
29602         if(this.isVisible()){
29603             this.el.setXY(this.xy);
29604             this.adjustAssets();
29605         }
29606         return this;
29607     },
29608
29609     /**
29610      * Anchors an element to another element and realigns it when the window is resized.
29611      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29612      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29613      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29614      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29615      * is a number, it is used as the buffer delay (defaults to 50ms).
29616      * @return {Roo.BasicDialog} this
29617      */
29618     anchorTo : function(el, alignment, offsets, monitorScroll){
29619         var action = function(){
29620             this.alignTo(el, alignment, offsets);
29621         };
29622         Roo.EventManager.onWindowResize(action, this);
29623         var tm = typeof monitorScroll;
29624         if(tm != 'undefined'){
29625             Roo.EventManager.on(window, 'scroll', action, this,
29626                 {buffer: tm == 'number' ? monitorScroll : 50});
29627         }
29628         action.call(this);
29629         return this;
29630     },
29631
29632     /**
29633      * Returns true if the dialog is visible
29634      * @return {Boolean}
29635      */
29636     isVisible : function(){
29637         return this.el.isVisible();
29638     },
29639
29640     // private
29641     animHide : function(callback){
29642         var b = Roo.get(this.animateTarget).getBox();
29643         this.proxy.show();
29644         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29645         this.el.hide();
29646         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29647                     this.hideEl.createDelegate(this, [callback]));
29648     },
29649
29650     /**
29651      * Hides the dialog.
29652      * @param {Function} callback (optional) Function to call when the dialog is hidden
29653      * @return {Roo.BasicDialog} this
29654      */
29655     hide : function(callback){
29656         if (this.fireEvent("beforehide", this) === false){
29657             return;
29658         }
29659         if(this.shadow){
29660             this.shadow.hide();
29661         }
29662         if(this.shim) {
29663           this.shim.hide();
29664         }
29665         // sometimes animateTarget seems to get set.. causing problems...
29666         // this just double checks..
29667         if(this.animateTarget && Roo.get(this.animateTarget)) {
29668            this.animHide(callback);
29669         }else{
29670             this.el.hide();
29671             this.hideEl(callback);
29672         }
29673         return this;
29674     },
29675
29676     // private
29677     hideEl : function(callback){
29678         this.proxy.hide();
29679         if(this.modal){
29680             this.mask.hide();
29681             Roo.get(document.body).removeClass("x-body-masked");
29682         }
29683         this.fireEvent("hide", this);
29684         if(typeof callback == "function"){
29685             callback();
29686         }
29687     },
29688
29689     // private
29690     hideAction : function(){
29691         this.setLeft("-10000px");
29692         this.setTop("-10000px");
29693         this.setStyle("visibility", "hidden");
29694     },
29695
29696     // private
29697     refreshSize : function(){
29698         this.size = this.el.getSize();
29699         this.xy = this.el.getXY();
29700         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29701     },
29702
29703     // private
29704     // z-index is managed by the DialogManager and may be overwritten at any time
29705     setZIndex : function(index){
29706         if(this.modal){
29707             this.mask.setStyle("z-index", index);
29708         }
29709         if(this.shim){
29710             this.shim.setStyle("z-index", ++index);
29711         }
29712         if(this.shadow){
29713             this.shadow.setZIndex(++index);
29714         }
29715         this.el.setStyle("z-index", ++index);
29716         if(this.proxy){
29717             this.proxy.setStyle("z-index", ++index);
29718         }
29719         if(this.resizer){
29720             this.resizer.proxy.setStyle("z-index", ++index);
29721         }
29722
29723         this.lastZIndex = index;
29724     },
29725
29726     /**
29727      * Returns the element for this dialog
29728      * @return {Roo.Element} The underlying dialog Element
29729      */
29730     getEl : function(){
29731         return this.el;
29732     }
29733 });
29734
29735 /**
29736  * @class Roo.DialogManager
29737  * Provides global access to BasicDialogs that have been created and
29738  * support for z-indexing (layering) multiple open dialogs.
29739  */
29740 Roo.DialogManager = function(){
29741     var list = {};
29742     var accessList = [];
29743     var front = null;
29744
29745     // private
29746     var sortDialogs = function(d1, d2){
29747         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29748     };
29749
29750     // private
29751     var orderDialogs = function(){
29752         accessList.sort(sortDialogs);
29753         var seed = Roo.DialogManager.zseed;
29754         for(var i = 0, len = accessList.length; i < len; i++){
29755             var dlg = accessList[i];
29756             if(dlg){
29757                 dlg.setZIndex(seed + (i*10));
29758             }
29759         }
29760     };
29761
29762     return {
29763         /**
29764          * The starting z-index for BasicDialogs (defaults to 9000)
29765          * @type Number The z-index value
29766          */
29767         zseed : 9000,
29768
29769         // private
29770         register : function(dlg){
29771             list[dlg.id] = dlg;
29772             accessList.push(dlg);
29773         },
29774
29775         // private
29776         unregister : function(dlg){
29777             delete list[dlg.id];
29778             var i=0;
29779             var len=0;
29780             if(!accessList.indexOf){
29781                 for(  i = 0, len = accessList.length; i < len; i++){
29782                     if(accessList[i] == dlg){
29783                         accessList.splice(i, 1);
29784                         return;
29785                     }
29786                 }
29787             }else{
29788                  i = accessList.indexOf(dlg);
29789                 if(i != -1){
29790                     accessList.splice(i, 1);
29791                 }
29792             }
29793         },
29794
29795         /**
29796          * Gets a registered dialog by id
29797          * @param {String/Object} id The id of the dialog or a dialog
29798          * @return {Roo.BasicDialog} this
29799          */
29800         get : function(id){
29801             return typeof id == "object" ? id : list[id];
29802         },
29803
29804         /**
29805          * Brings the specified dialog to the front
29806          * @param {String/Object} dlg The id of the dialog or a dialog
29807          * @return {Roo.BasicDialog} this
29808          */
29809         bringToFront : function(dlg){
29810             dlg = this.get(dlg);
29811             if(dlg != front){
29812                 front = dlg;
29813                 dlg._lastAccess = new Date().getTime();
29814                 orderDialogs();
29815             }
29816             return dlg;
29817         },
29818
29819         /**
29820          * Sends the specified dialog to the back
29821          * @param {String/Object} dlg The id of the dialog or a dialog
29822          * @return {Roo.BasicDialog} this
29823          */
29824         sendToBack : function(dlg){
29825             dlg = this.get(dlg);
29826             dlg._lastAccess = -(new Date().getTime());
29827             orderDialogs();
29828             return dlg;
29829         },
29830
29831         /**
29832          * Hides all dialogs
29833          */
29834         hideAll : function(){
29835             for(var id in list){
29836                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29837                     list[id].hide();
29838                 }
29839             }
29840         }
29841     };
29842 }();
29843
29844 /**
29845  * @class Roo.LayoutDialog
29846  * @extends Roo.BasicDialog
29847  * Dialog which provides adjustments for working with a layout in a Dialog.
29848  * Add your necessary layout config options to the dialog's config.<br>
29849  * Example usage (including a nested layout):
29850  * <pre><code>
29851 if(!dialog){
29852     dialog = new Roo.LayoutDialog("download-dlg", {
29853         modal: true,
29854         width:600,
29855         height:450,
29856         shadow:true,
29857         minWidth:500,
29858         minHeight:350,
29859         autoTabs:true,
29860         proxyDrag:true,
29861         // layout config merges with the dialog config
29862         center:{
29863             tabPosition: "top",
29864             alwaysShowTabs: true
29865         }
29866     });
29867     dialog.addKeyListener(27, dialog.hide, dialog);
29868     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29869     dialog.addButton("Build It!", this.getDownload, this);
29870
29871     // we can even add nested layouts
29872     var innerLayout = new Roo.BorderLayout("dl-inner", {
29873         east: {
29874             initialSize: 200,
29875             autoScroll:true,
29876             split:true
29877         },
29878         center: {
29879             autoScroll:true
29880         }
29881     });
29882     innerLayout.beginUpdate();
29883     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29884     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29885     innerLayout.endUpdate(true);
29886
29887     var layout = dialog.getLayout();
29888     layout.beginUpdate();
29889     layout.add("center", new Roo.ContentPanel("standard-panel",
29890                         {title: "Download the Source", fitToFrame:true}));
29891     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29892                {title: "Build your own roo.js"}));
29893     layout.getRegion("center").showPanel(sp);
29894     layout.endUpdate();
29895 }
29896 </code></pre>
29897     * @constructor
29898     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29899     * @param {Object} config configuration options
29900   */
29901 Roo.LayoutDialog = function(el, cfg){
29902     
29903     var config=  cfg;
29904     if (typeof(cfg) == 'undefined') {
29905         config = Roo.apply({}, el);
29906         // not sure why we use documentElement here.. - it should always be body.
29907         // IE7 borks horribly if we use documentElement.
29908         // webkit also does not like documentElement - it creates a body element...
29909         el = Roo.get( document.body || document.documentElement ).createChild();
29910         //config.autoCreate = true;
29911     }
29912     
29913     
29914     config.autoTabs = false;
29915     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29916     this.body.setStyle({overflow:"hidden", position:"relative"});
29917     this.layout = new Roo.BorderLayout(this.body.dom, config);
29918     this.layout.monitorWindowResize = false;
29919     this.el.addClass("x-dlg-auto-layout");
29920     // fix case when center region overwrites center function
29921     this.center = Roo.BasicDialog.prototype.center;
29922     this.on("show", this.layout.layout, this.layout, true);
29923     if (config.items) {
29924         var xitems = config.items;
29925         delete config.items;
29926         Roo.each(xitems, this.addxtype, this);
29927     }
29928     
29929     
29930 };
29931 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29932     /**
29933      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29934      * @deprecated
29935      */
29936     endUpdate : function(){
29937         this.layout.endUpdate();
29938     },
29939
29940     /**
29941      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29942      *  @deprecated
29943      */
29944     beginUpdate : function(){
29945         this.layout.beginUpdate();
29946     },
29947
29948     /**
29949      * Get the BorderLayout for this dialog
29950      * @return {Roo.BorderLayout}
29951      */
29952     getLayout : function(){
29953         return this.layout;
29954     },
29955
29956     showEl : function(){
29957         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29958         if(Roo.isIE7){
29959             this.layout.layout();
29960         }
29961     },
29962
29963     // private
29964     // Use the syncHeightBeforeShow config option to control this automatically
29965     syncBodyHeight : function(){
29966         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29967         if(this.layout){this.layout.layout();}
29968     },
29969     
29970       /**
29971      * Add an xtype element (actually adds to the layout.)
29972      * @return {Object} xdata xtype object data.
29973      */
29974     
29975     addxtype : function(c) {
29976         return this.layout.addxtype(c);
29977     }
29978 });/*
29979  * Based on:
29980  * Ext JS Library 1.1.1
29981  * Copyright(c) 2006-2007, Ext JS, LLC.
29982  *
29983  * Originally Released Under LGPL - original licence link has changed is not relivant.
29984  *
29985  * Fork - LGPL
29986  * <script type="text/javascript">
29987  */
29988  
29989 /**
29990  * @class Roo.MessageBox
29991  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29992  * Example usage:
29993  *<pre><code>
29994 // Basic alert:
29995 Roo.Msg.alert('Status', 'Changes saved successfully.');
29996
29997 // Prompt for user data:
29998 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29999     if (btn == 'ok'){
30000         // process text value...
30001     }
30002 });
30003
30004 // Show a dialog using config options:
30005 Roo.Msg.show({
30006    title:'Save Changes?',
30007    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30008    buttons: Roo.Msg.YESNOCANCEL,
30009    fn: processResult,
30010    animEl: 'elId'
30011 });
30012 </code></pre>
30013  * @singleton
30014  */
30015 Roo.MessageBox = function(){
30016     var dlg, opt, mask, waitTimer;
30017     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30018     var buttons, activeTextEl, bwidth;
30019
30020     // private
30021     var handleButton = function(button){
30022         dlg.hide();
30023         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30024     };
30025
30026     // private
30027     var handleHide = function(){
30028         if(opt && opt.cls){
30029             dlg.el.removeClass(opt.cls);
30030         }
30031         if(waitTimer){
30032             Roo.TaskMgr.stop(waitTimer);
30033             waitTimer = null;
30034         }
30035     };
30036
30037     // private
30038     var updateButtons = function(b){
30039         var width = 0;
30040         if(!b){
30041             buttons["ok"].hide();
30042             buttons["cancel"].hide();
30043             buttons["yes"].hide();
30044             buttons["no"].hide();
30045             dlg.footer.dom.style.display = 'none';
30046             return width;
30047         }
30048         dlg.footer.dom.style.display = '';
30049         for(var k in buttons){
30050             if(typeof buttons[k] != "function"){
30051                 if(b[k]){
30052                     buttons[k].show();
30053                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30054                     width += buttons[k].el.getWidth()+15;
30055                 }else{
30056                     buttons[k].hide();
30057                 }
30058             }
30059         }
30060         return width;
30061     };
30062
30063     // private
30064     var handleEsc = function(d, k, e){
30065         if(opt && opt.closable !== false){
30066             dlg.hide();
30067         }
30068         if(e){
30069             e.stopEvent();
30070         }
30071     };
30072
30073     return {
30074         /**
30075          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30076          * @return {Roo.BasicDialog} The BasicDialog element
30077          */
30078         getDialog : function(){
30079            if(!dlg){
30080                 dlg = new Roo.BasicDialog("x-msg-box", {
30081                     autoCreate : true,
30082                     shadow: true,
30083                     draggable: true,
30084                     resizable:false,
30085                     constraintoviewport:false,
30086                     fixedcenter:true,
30087                     collapsible : false,
30088                     shim:true,
30089                     modal: true,
30090                     width:400, height:100,
30091                     buttonAlign:"center",
30092                     closeClick : function(){
30093                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30094                             handleButton("no");
30095                         }else{
30096                             handleButton("cancel");
30097                         }
30098                     }
30099                 });
30100                 dlg.on("hide", handleHide);
30101                 mask = dlg.mask;
30102                 dlg.addKeyListener(27, handleEsc);
30103                 buttons = {};
30104                 var bt = this.buttonText;
30105                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30106                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30107                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30108                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30109                 bodyEl = dlg.body.createChild({
30110
30111                     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>'
30112                 });
30113                 msgEl = bodyEl.dom.firstChild;
30114                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30115                 textboxEl.enableDisplayMode();
30116                 textboxEl.addKeyListener([10,13], function(){
30117                     if(dlg.isVisible() && opt && opt.buttons){
30118                         if(opt.buttons.ok){
30119                             handleButton("ok");
30120                         }else if(opt.buttons.yes){
30121                             handleButton("yes");
30122                         }
30123                     }
30124                 });
30125                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30126                 textareaEl.enableDisplayMode();
30127                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30128                 progressEl.enableDisplayMode();
30129                 var pf = progressEl.dom.firstChild;
30130                 if (pf) {
30131                     pp = Roo.get(pf.firstChild);
30132                     pp.setHeight(pf.offsetHeight);
30133                 }
30134                 
30135             }
30136             return dlg;
30137         },
30138
30139         /**
30140          * Updates the message box body text
30141          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30142          * the XHTML-compliant non-breaking space character '&amp;#160;')
30143          * @return {Roo.MessageBox} This message box
30144          */
30145         updateText : function(text){
30146             if(!dlg.isVisible() && !opt.width){
30147                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30148             }
30149             msgEl.innerHTML = text || '&#160;';
30150       
30151             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30152             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30153             var w = Math.max(
30154                     Math.min(opt.width || cw , this.maxWidth), 
30155                     Math.max(opt.minWidth || this.minWidth, bwidth)
30156             );
30157             if(opt.prompt){
30158                 activeTextEl.setWidth(w);
30159             }
30160             if(dlg.isVisible()){
30161                 dlg.fixedcenter = false;
30162             }
30163             // to big, make it scroll. = But as usual stupid IE does not support
30164             // !important..
30165             
30166             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30167                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30168                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30169             } else {
30170                 bodyEl.dom.style.height = '';
30171                 bodyEl.dom.style.overflowY = '';
30172             }
30173             if (cw > w) {
30174                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30175             } else {
30176                 bodyEl.dom.style.overflowX = '';
30177             }
30178             
30179             dlg.setContentSize(w, bodyEl.getHeight());
30180             if(dlg.isVisible()){
30181                 dlg.fixedcenter = true;
30182             }
30183             return this;
30184         },
30185
30186         /**
30187          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30188          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30189          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30190          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30191          * @return {Roo.MessageBox} This message box
30192          */
30193         updateProgress : function(value, text){
30194             if(text){
30195                 this.updateText(text);
30196             }
30197             if (pp) { // weird bug on my firefox - for some reason this is not defined
30198                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30199             }
30200             return this;
30201         },        
30202
30203         /**
30204          * Returns true if the message box is currently displayed
30205          * @return {Boolean} True if the message box is visible, else false
30206          */
30207         isVisible : function(){
30208             return dlg && dlg.isVisible();  
30209         },
30210
30211         /**
30212          * Hides the message box if it is displayed
30213          */
30214         hide : function(){
30215             if(this.isVisible()){
30216                 dlg.hide();
30217             }  
30218         },
30219
30220         /**
30221          * Displays a new message box, or reinitializes an existing message box, based on the config options
30222          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30223          * The following config object properties are supported:
30224          * <pre>
30225 Property    Type             Description
30226 ----------  ---------------  ------------------------------------------------------------------------------------
30227 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30228                                    closes (defaults to undefined)
30229 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30230                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30231 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30232                                    progress and wait dialogs will ignore this property and always hide the
30233                                    close button as they can only be closed programmatically.
30234 cls               String           A custom CSS class to apply to the message box element
30235 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30236                                    displayed (defaults to 75)
30237 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30238                                    function will be btn (the name of the button that was clicked, if applicable,
30239                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30240                                    Progress and wait dialogs will ignore this option since they do not respond to
30241                                    user actions and can only be closed programmatically, so any required function
30242                                    should be called by the same code after it closes the dialog.
30243 icon              String           A CSS class that provides a background image to be used as an icon for
30244                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30245 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30246 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30247 modal             Boolean          False to allow user interaction with the page while the message box is
30248                                    displayed (defaults to true)
30249 msg               String           A string that will replace the existing message box body text (defaults
30250                                    to the XHTML-compliant non-breaking space character '&#160;')
30251 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30252 progress          Boolean          True to display a progress bar (defaults to false)
30253 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30254 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30255 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30256 title             String           The title text
30257 value             String           The string value to set into the active textbox element if displayed
30258 wait              Boolean          True to display a progress bar (defaults to false)
30259 width             Number           The width of the dialog in pixels
30260 </pre>
30261          *
30262          * Example usage:
30263          * <pre><code>
30264 Roo.Msg.show({
30265    title: 'Address',
30266    msg: 'Please enter your address:',
30267    width: 300,
30268    buttons: Roo.MessageBox.OKCANCEL,
30269    multiline: true,
30270    fn: saveAddress,
30271    animEl: 'addAddressBtn'
30272 });
30273 </code></pre>
30274          * @param {Object} config Configuration options
30275          * @return {Roo.MessageBox} This message box
30276          */
30277         show : function(options)
30278         {
30279             
30280             // this causes nightmares if you show one dialog after another
30281             // especially on callbacks..
30282              
30283             if(this.isVisible()){
30284                 
30285                 this.hide();
30286                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30287                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30288                 Roo.log("New Dialog Message:" +  options.msg )
30289                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30290                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30291                 
30292             }
30293             var d = this.getDialog();
30294             opt = options;
30295             d.setTitle(opt.title || "&#160;");
30296             d.close.setDisplayed(opt.closable !== false);
30297             activeTextEl = textboxEl;
30298             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30299             if(opt.prompt){
30300                 if(opt.multiline){
30301                     textboxEl.hide();
30302                     textareaEl.show();
30303                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30304                         opt.multiline : this.defaultTextHeight);
30305                     activeTextEl = textareaEl;
30306                 }else{
30307                     textboxEl.show();
30308                     textareaEl.hide();
30309                 }
30310             }else{
30311                 textboxEl.hide();
30312                 textareaEl.hide();
30313             }
30314             progressEl.setDisplayed(opt.progress === true);
30315             this.updateProgress(0);
30316             activeTextEl.dom.value = opt.value || "";
30317             if(opt.prompt){
30318                 dlg.setDefaultButton(activeTextEl);
30319             }else{
30320                 var bs = opt.buttons;
30321                 var db = null;
30322                 if(bs && bs.ok){
30323                     db = buttons["ok"];
30324                 }else if(bs && bs.yes){
30325                     db = buttons["yes"];
30326                 }
30327                 dlg.setDefaultButton(db);
30328             }
30329             bwidth = updateButtons(opt.buttons);
30330             this.updateText(opt.msg);
30331             if(opt.cls){
30332                 d.el.addClass(opt.cls);
30333             }
30334             d.proxyDrag = opt.proxyDrag === true;
30335             d.modal = opt.modal !== false;
30336             d.mask = opt.modal !== false ? mask : false;
30337             if(!d.isVisible()){
30338                 // force it to the end of the z-index stack so it gets a cursor in FF
30339                 document.body.appendChild(dlg.el.dom);
30340                 d.animateTarget = null;
30341                 d.show(options.animEl);
30342             }
30343             return this;
30344         },
30345
30346         /**
30347          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30348          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30349          * and closing the message box when the process is complete.
30350          * @param {String} title The title bar text
30351          * @param {String} msg The message box body text
30352          * @return {Roo.MessageBox} This message box
30353          */
30354         progress : function(title, msg){
30355             this.show({
30356                 title : title,
30357                 msg : msg,
30358                 buttons: false,
30359                 progress:true,
30360                 closable:false,
30361                 minWidth: this.minProgressWidth,
30362                 modal : true
30363             });
30364             return this;
30365         },
30366
30367         /**
30368          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30369          * If a callback function is passed it will be called after the user clicks the button, and the
30370          * id of the button that was clicked will be passed as the only parameter to the callback
30371          * (could also be the top-right close button).
30372          * @param {String} title The title bar text
30373          * @param {String} msg The message box body text
30374          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30375          * @param {Object} scope (optional) The scope of the callback function
30376          * @return {Roo.MessageBox} This message box
30377          */
30378         alert : function(title, msg, fn, scope){
30379             this.show({
30380                 title : title,
30381                 msg : msg,
30382                 buttons: this.OK,
30383                 fn: fn,
30384                 scope : scope,
30385                 modal : true
30386             });
30387             return this;
30388         },
30389
30390         /**
30391          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30392          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30393          * You are responsible for closing the message box when the process is complete.
30394          * @param {String} msg The message box body text
30395          * @param {String} title (optional) The title bar text
30396          * @return {Roo.MessageBox} This message box
30397          */
30398         wait : function(msg, title){
30399             this.show({
30400                 title : title,
30401                 msg : msg,
30402                 buttons: false,
30403                 closable:false,
30404                 progress:true,
30405                 modal:true,
30406                 width:300,
30407                 wait:true
30408             });
30409             waitTimer = Roo.TaskMgr.start({
30410                 run: function(i){
30411                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30412                 },
30413                 interval: 1000
30414             });
30415             return this;
30416         },
30417
30418         /**
30419          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30420          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30421          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30422          * @param {String} title The title bar text
30423          * @param {String} msg The message box body text
30424          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30425          * @param {Object} scope (optional) The scope of the callback function
30426          * @return {Roo.MessageBox} This message box
30427          */
30428         confirm : function(title, msg, fn, scope){
30429             this.show({
30430                 title : title,
30431                 msg : msg,
30432                 buttons: this.YESNO,
30433                 fn: fn,
30434                 scope : scope,
30435                 modal : true
30436             });
30437             return this;
30438         },
30439
30440         /**
30441          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30442          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30443          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30444          * (could also be the top-right close button) and the text that was entered will be passed as the two
30445          * parameters to the callback.
30446          * @param {String} title The title bar text
30447          * @param {String} msg The message box body text
30448          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30449          * @param {Object} scope (optional) The scope of the callback function
30450          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30451          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30452          * @return {Roo.MessageBox} This message box
30453          */
30454         prompt : function(title, msg, fn, scope, multiline){
30455             this.show({
30456                 title : title,
30457                 msg : msg,
30458                 buttons: this.OKCANCEL,
30459                 fn: fn,
30460                 minWidth:250,
30461                 scope : scope,
30462                 prompt:true,
30463                 multiline: multiline,
30464                 modal : true
30465             });
30466             return this;
30467         },
30468
30469         /**
30470          * Button config that displays a single OK button
30471          * @type Object
30472          */
30473         OK : {ok:true},
30474         /**
30475          * Button config that displays Yes and No buttons
30476          * @type Object
30477          */
30478         YESNO : {yes:true, no:true},
30479         /**
30480          * Button config that displays OK and Cancel buttons
30481          * @type Object
30482          */
30483         OKCANCEL : {ok:true, cancel:true},
30484         /**
30485          * Button config that displays Yes, No and Cancel buttons
30486          * @type Object
30487          */
30488         YESNOCANCEL : {yes:true, no:true, cancel:true},
30489
30490         /**
30491          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30492          * @type Number
30493          */
30494         defaultTextHeight : 75,
30495         /**
30496          * The maximum width in pixels of the message box (defaults to 600)
30497          * @type Number
30498          */
30499         maxWidth : 600,
30500         /**
30501          * The minimum width in pixels of the message box (defaults to 100)
30502          * @type Number
30503          */
30504         minWidth : 100,
30505         /**
30506          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30507          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30508          * @type Number
30509          */
30510         minProgressWidth : 250,
30511         /**
30512          * An object containing the default button text strings that can be overriden for localized language support.
30513          * Supported properties are: ok, cancel, yes and no.
30514          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30515          * @type Object
30516          */
30517         buttonText : {
30518             ok : "OK",
30519             cancel : "Cancel",
30520             yes : "Yes",
30521             no : "No"
30522         }
30523     };
30524 }();
30525
30526 /**
30527  * Shorthand for {@link Roo.MessageBox}
30528  */
30529 Roo.Msg = Roo.MessageBox;/*
30530  * Based on:
30531  * Ext JS Library 1.1.1
30532  * Copyright(c) 2006-2007, Ext JS, LLC.
30533  *
30534  * Originally Released Under LGPL - original licence link has changed is not relivant.
30535  *
30536  * Fork - LGPL
30537  * <script type="text/javascript">
30538  */
30539 /**
30540  * @class Roo.QuickTips
30541  * Provides attractive and customizable tooltips for any element.
30542  * @singleton
30543  */
30544 Roo.QuickTips = function(){
30545     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30546     var ce, bd, xy, dd;
30547     var visible = false, disabled = true, inited = false;
30548     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30549     
30550     var onOver = function(e){
30551         if(disabled){
30552             return;
30553         }
30554         var t = e.getTarget();
30555         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30556             return;
30557         }
30558         if(ce && t == ce.el){
30559             clearTimeout(hideProc);
30560             return;
30561         }
30562         if(t && tagEls[t.id]){
30563             tagEls[t.id].el = t;
30564             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30565             return;
30566         }
30567         var ttp, et = Roo.fly(t);
30568         var ns = cfg.namespace;
30569         if(tm.interceptTitles && t.title){
30570             ttp = t.title;
30571             t.qtip = ttp;
30572             t.removeAttribute("title");
30573             e.preventDefault();
30574         }else{
30575             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30576         }
30577         if(ttp){
30578             showProc = show.defer(tm.showDelay, tm, [{
30579                 el: t, 
30580                 text: ttp, 
30581                 width: et.getAttributeNS(ns, cfg.width),
30582                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30583                 title: et.getAttributeNS(ns, cfg.title),
30584                     cls: et.getAttributeNS(ns, cfg.cls)
30585             }]);
30586         }
30587     };
30588     
30589     var onOut = function(e){
30590         clearTimeout(showProc);
30591         var t = e.getTarget();
30592         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30593             hideProc = setTimeout(hide, tm.hideDelay);
30594         }
30595     };
30596     
30597     var onMove = function(e){
30598         if(disabled){
30599             return;
30600         }
30601         xy = e.getXY();
30602         xy[1] += 18;
30603         if(tm.trackMouse && ce){
30604             el.setXY(xy);
30605         }
30606     };
30607     
30608     var onDown = function(e){
30609         clearTimeout(showProc);
30610         clearTimeout(hideProc);
30611         if(!e.within(el)){
30612             if(tm.hideOnClick){
30613                 hide();
30614                 tm.disable();
30615                 tm.enable.defer(100, tm);
30616             }
30617         }
30618     };
30619     
30620     var getPad = function(){
30621         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30622     };
30623
30624     var show = function(o){
30625         if(disabled){
30626             return;
30627         }
30628         clearTimeout(dismissProc);
30629         ce = o;
30630         if(removeCls){ // in case manually hidden
30631             el.removeClass(removeCls);
30632             removeCls = null;
30633         }
30634         if(ce.cls){
30635             el.addClass(ce.cls);
30636             removeCls = ce.cls;
30637         }
30638         if(ce.title){
30639             tipTitle.update(ce.title);
30640             tipTitle.show();
30641         }else{
30642             tipTitle.update('');
30643             tipTitle.hide();
30644         }
30645         el.dom.style.width  = tm.maxWidth+'px';
30646         //tipBody.dom.style.width = '';
30647         tipBodyText.update(o.text);
30648         var p = getPad(), w = ce.width;
30649         if(!w){
30650             var td = tipBodyText.dom;
30651             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30652             if(aw > tm.maxWidth){
30653                 w = tm.maxWidth;
30654             }else if(aw < tm.minWidth){
30655                 w = tm.minWidth;
30656             }else{
30657                 w = aw;
30658             }
30659         }
30660         //tipBody.setWidth(w);
30661         el.setWidth(parseInt(w, 10) + p);
30662         if(ce.autoHide === false){
30663             close.setDisplayed(true);
30664             if(dd){
30665                 dd.unlock();
30666             }
30667         }else{
30668             close.setDisplayed(false);
30669             if(dd){
30670                 dd.lock();
30671             }
30672         }
30673         if(xy){
30674             el.avoidY = xy[1]-18;
30675             el.setXY(xy);
30676         }
30677         if(tm.animate){
30678             el.setOpacity(.1);
30679             el.setStyle("visibility", "visible");
30680             el.fadeIn({callback: afterShow});
30681         }else{
30682             afterShow();
30683         }
30684     };
30685     
30686     var afterShow = function(){
30687         if(ce){
30688             el.show();
30689             esc.enable();
30690             if(tm.autoDismiss && ce.autoHide !== false){
30691                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30692             }
30693         }
30694     };
30695     
30696     var hide = function(noanim){
30697         clearTimeout(dismissProc);
30698         clearTimeout(hideProc);
30699         ce = null;
30700         if(el.isVisible()){
30701             esc.disable();
30702             if(noanim !== true && tm.animate){
30703                 el.fadeOut({callback: afterHide});
30704             }else{
30705                 afterHide();
30706             } 
30707         }
30708     };
30709     
30710     var afterHide = function(){
30711         el.hide();
30712         if(removeCls){
30713             el.removeClass(removeCls);
30714             removeCls = null;
30715         }
30716     };
30717     
30718     return {
30719         /**
30720         * @cfg {Number} minWidth
30721         * The minimum width of the quick tip (defaults to 40)
30722         */
30723        minWidth : 40,
30724         /**
30725         * @cfg {Number} maxWidth
30726         * The maximum width of the quick tip (defaults to 300)
30727         */
30728        maxWidth : 300,
30729         /**
30730         * @cfg {Boolean} interceptTitles
30731         * True to automatically use the element's DOM title value if available (defaults to false)
30732         */
30733        interceptTitles : false,
30734         /**
30735         * @cfg {Boolean} trackMouse
30736         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30737         */
30738        trackMouse : false,
30739         /**
30740         * @cfg {Boolean} hideOnClick
30741         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30742         */
30743        hideOnClick : true,
30744         /**
30745         * @cfg {Number} showDelay
30746         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30747         */
30748        showDelay : 500,
30749         /**
30750         * @cfg {Number} hideDelay
30751         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30752         */
30753        hideDelay : 200,
30754         /**
30755         * @cfg {Boolean} autoHide
30756         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30757         * Used in conjunction with hideDelay.
30758         */
30759        autoHide : true,
30760         /**
30761         * @cfg {Boolean}
30762         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30763         * (defaults to true).  Used in conjunction with autoDismissDelay.
30764         */
30765        autoDismiss : true,
30766         /**
30767         * @cfg {Number}
30768         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30769         */
30770        autoDismissDelay : 5000,
30771        /**
30772         * @cfg {Boolean} animate
30773         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30774         */
30775        animate : false,
30776
30777        /**
30778         * @cfg {String} title
30779         * Title text to display (defaults to '').  This can be any valid HTML markup.
30780         */
30781         title: '',
30782        /**
30783         * @cfg {String} text
30784         * Body text to display (defaults to '').  This can be any valid HTML markup.
30785         */
30786         text : '',
30787        /**
30788         * @cfg {String} cls
30789         * A CSS class to apply to the base quick tip element (defaults to '').
30790         */
30791         cls : '',
30792        /**
30793         * @cfg {Number} width
30794         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30795         * minWidth or maxWidth.
30796         */
30797         width : null,
30798
30799     /**
30800      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30801      * or display QuickTips in a page.
30802      */
30803        init : function(){
30804           tm = Roo.QuickTips;
30805           cfg = tm.tagConfig;
30806           if(!inited){
30807               if(!Roo.isReady){ // allow calling of init() before onReady
30808                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30809                   return;
30810               }
30811               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30812               el.fxDefaults = {stopFx: true};
30813               // maximum custom styling
30814               //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>');
30815               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>');              
30816               tipTitle = el.child('h3');
30817               tipTitle.enableDisplayMode("block");
30818               tipBody = el.child('div.x-tip-bd');
30819               tipBodyText = el.child('div.x-tip-bd-inner');
30820               //bdLeft = el.child('div.x-tip-bd-left');
30821               //bdRight = el.child('div.x-tip-bd-right');
30822               close = el.child('div.x-tip-close');
30823               close.enableDisplayMode("block");
30824               close.on("click", hide);
30825               var d = Roo.get(document);
30826               d.on("mousedown", onDown);
30827               d.on("mouseover", onOver);
30828               d.on("mouseout", onOut);
30829               d.on("mousemove", onMove);
30830               esc = d.addKeyListener(27, hide);
30831               esc.disable();
30832               if(Roo.dd.DD){
30833                   dd = el.initDD("default", null, {
30834                       onDrag : function(){
30835                           el.sync();  
30836                       }
30837                   });
30838                   dd.setHandleElId(tipTitle.id);
30839                   dd.lock();
30840               }
30841               inited = true;
30842           }
30843           this.enable(); 
30844        },
30845
30846     /**
30847      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30848      * are supported:
30849      * <pre>
30850 Property    Type                   Description
30851 ----------  ---------------------  ------------------------------------------------------------------------
30852 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30853      * </ul>
30854      * @param {Object} config The config object
30855      */
30856        register : function(config){
30857            var cs = config instanceof Array ? config : arguments;
30858            for(var i = 0, len = cs.length; i < len; i++) {
30859                var c = cs[i];
30860                var target = c.target;
30861                if(target){
30862                    if(target instanceof Array){
30863                        for(var j = 0, jlen = target.length; j < jlen; j++){
30864                            tagEls[target[j]] = c;
30865                        }
30866                    }else{
30867                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30868                    }
30869                }
30870            }
30871        },
30872
30873     /**
30874      * Removes this quick tip from its element and destroys it.
30875      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30876      */
30877        unregister : function(el){
30878            delete tagEls[Roo.id(el)];
30879        },
30880
30881     /**
30882      * Enable this quick tip.
30883      */
30884        enable : function(){
30885            if(inited && disabled){
30886                locks.pop();
30887                if(locks.length < 1){
30888                    disabled = false;
30889                }
30890            }
30891        },
30892
30893     /**
30894      * Disable this quick tip.
30895      */
30896        disable : function(){
30897           disabled = true;
30898           clearTimeout(showProc);
30899           clearTimeout(hideProc);
30900           clearTimeout(dismissProc);
30901           if(ce){
30902               hide(true);
30903           }
30904           locks.push(1);
30905        },
30906
30907     /**
30908      * Returns true if the quick tip is enabled, else false.
30909      */
30910        isEnabled : function(){
30911             return !disabled;
30912        },
30913
30914         // private
30915        tagConfig : {
30916            namespace : "ext",
30917            attribute : "qtip",
30918            width : "width",
30919            target : "target",
30920            title : "qtitle",
30921            hide : "hide",
30922            cls : "qclass"
30923        }
30924    };
30925 }();
30926
30927 // backwards compat
30928 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30929  * Based on:
30930  * Ext JS Library 1.1.1
30931  * Copyright(c) 2006-2007, Ext JS, LLC.
30932  *
30933  * Originally Released Under LGPL - original licence link has changed is not relivant.
30934  *
30935  * Fork - LGPL
30936  * <script type="text/javascript">
30937  */
30938  
30939
30940 /**
30941  * @class Roo.tree.TreePanel
30942  * @extends Roo.data.Tree
30943
30944  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30945  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30946  * @cfg {Boolean} enableDD true to enable drag and drop
30947  * @cfg {Boolean} enableDrag true to enable just drag
30948  * @cfg {Boolean} enableDrop true to enable just drop
30949  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30950  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30951  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30952  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30953  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30954  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30955  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30956  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30957  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30958  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30959  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30960  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30961  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30962  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30963  * @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>
30964  * @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>
30965  * 
30966  * @constructor
30967  * @param {String/HTMLElement/Element} el The container element
30968  * @param {Object} config
30969  */
30970 Roo.tree.TreePanel = function(el, config){
30971     var root = false;
30972     var loader = false;
30973     if (config.root) {
30974         root = config.root;
30975         delete config.root;
30976     }
30977     if (config.loader) {
30978         loader = config.loader;
30979         delete config.loader;
30980     }
30981     
30982     Roo.apply(this, config);
30983     Roo.tree.TreePanel.superclass.constructor.call(this);
30984     this.el = Roo.get(el);
30985     this.el.addClass('x-tree');
30986     //console.log(root);
30987     if (root) {
30988         this.setRootNode( Roo.factory(root, Roo.tree));
30989     }
30990     if (loader) {
30991         this.loader = Roo.factory(loader, Roo.tree);
30992     }
30993    /**
30994     * Read-only. The id of the container element becomes this TreePanel's id.
30995     */
30996     this.id = this.el.id;
30997     this.addEvents({
30998         /**
30999         * @event beforeload
31000         * Fires before a node is loaded, return false to cancel
31001         * @param {Node} node The node being loaded
31002         */
31003         "beforeload" : true,
31004         /**
31005         * @event load
31006         * Fires when a node is loaded
31007         * @param {Node} node The node that was loaded
31008         */
31009         "load" : true,
31010         /**
31011         * @event textchange
31012         * Fires when the text for a node is changed
31013         * @param {Node} node The node
31014         * @param {String} text The new text
31015         * @param {String} oldText The old text
31016         */
31017         "textchange" : true,
31018         /**
31019         * @event beforeexpand
31020         * Fires before a node is expanded, return false to cancel.
31021         * @param {Node} node The node
31022         * @param {Boolean} deep
31023         * @param {Boolean} anim
31024         */
31025         "beforeexpand" : true,
31026         /**
31027         * @event beforecollapse
31028         * Fires before a node is collapsed, return false to cancel.
31029         * @param {Node} node The node
31030         * @param {Boolean} deep
31031         * @param {Boolean} anim
31032         */
31033         "beforecollapse" : true,
31034         /**
31035         * @event expand
31036         * Fires when a node is expanded
31037         * @param {Node} node The node
31038         */
31039         "expand" : true,
31040         /**
31041         * @event disabledchange
31042         * Fires when the disabled status of a node changes
31043         * @param {Node} node The node
31044         * @param {Boolean} disabled
31045         */
31046         "disabledchange" : true,
31047         /**
31048         * @event collapse
31049         * Fires when a node is collapsed
31050         * @param {Node} node The node
31051         */
31052         "collapse" : true,
31053         /**
31054         * @event beforeclick
31055         * Fires before click processing on a node. Return false to cancel the default action.
31056         * @param {Node} node The node
31057         * @param {Roo.EventObject} e The event object
31058         */
31059         "beforeclick":true,
31060         /**
31061         * @event checkchange
31062         * Fires when a node with a checkbox's checked property changes
31063         * @param {Node} this This node
31064         * @param {Boolean} checked
31065         */
31066         "checkchange":true,
31067         /**
31068         * @event click
31069         * Fires when a node is clicked
31070         * @param {Node} node The node
31071         * @param {Roo.EventObject} e The event object
31072         */
31073         "click":true,
31074         /**
31075         * @event dblclick
31076         * Fires when a node is double clicked
31077         * @param {Node} node The node
31078         * @param {Roo.EventObject} e The event object
31079         */
31080         "dblclick":true,
31081         /**
31082         * @event contextmenu
31083         * Fires when a node is right clicked
31084         * @param {Node} node The node
31085         * @param {Roo.EventObject} e The event object
31086         */
31087         "contextmenu":true,
31088         /**
31089         * @event beforechildrenrendered
31090         * Fires right before the child nodes for a node are rendered
31091         * @param {Node} node The node
31092         */
31093         "beforechildrenrendered":true,
31094         /**
31095         * @event startdrag
31096         * Fires when a node starts being dragged
31097         * @param {Roo.tree.TreePanel} this
31098         * @param {Roo.tree.TreeNode} node
31099         * @param {event} e The raw browser event
31100         */ 
31101        "startdrag" : true,
31102        /**
31103         * @event enddrag
31104         * Fires when a drag operation is complete
31105         * @param {Roo.tree.TreePanel} this
31106         * @param {Roo.tree.TreeNode} node
31107         * @param {event} e The raw browser event
31108         */
31109        "enddrag" : true,
31110        /**
31111         * @event dragdrop
31112         * Fires when a dragged node is dropped on a valid DD target
31113         * @param {Roo.tree.TreePanel} this
31114         * @param {Roo.tree.TreeNode} node
31115         * @param {DD} dd The dd it was dropped on
31116         * @param {event} e The raw browser event
31117         */
31118        "dragdrop" : true,
31119        /**
31120         * @event beforenodedrop
31121         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31122         * passed to handlers has the following properties:<br />
31123         * <ul style="padding:5px;padding-left:16px;">
31124         * <li>tree - The TreePanel</li>
31125         * <li>target - The node being targeted for the drop</li>
31126         * <li>data - The drag data from the drag source</li>
31127         * <li>point - The point of the drop - append, above or below</li>
31128         * <li>source - The drag source</li>
31129         * <li>rawEvent - Raw mouse event</li>
31130         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31131         * to be inserted by setting them on this object.</li>
31132         * <li>cancel - Set this to true to cancel the drop.</li>
31133         * </ul>
31134         * @param {Object} dropEvent
31135         */
31136        "beforenodedrop" : true,
31137        /**
31138         * @event nodedrop
31139         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31140         * passed to handlers has the following properties:<br />
31141         * <ul style="padding:5px;padding-left:16px;">
31142         * <li>tree - The TreePanel</li>
31143         * <li>target - The node being targeted for the drop</li>
31144         * <li>data - The drag data from the drag source</li>
31145         * <li>point - The point of the drop - append, above or below</li>
31146         * <li>source - The drag source</li>
31147         * <li>rawEvent - Raw mouse event</li>
31148         * <li>dropNode - Dropped node(s).</li>
31149         * </ul>
31150         * @param {Object} dropEvent
31151         */
31152        "nodedrop" : true,
31153         /**
31154         * @event nodedragover
31155         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31156         * passed to handlers has the following properties:<br />
31157         * <ul style="padding:5px;padding-left:16px;">
31158         * <li>tree - The TreePanel</li>
31159         * <li>target - The node being targeted for the drop</li>
31160         * <li>data - The drag data from the drag source</li>
31161         * <li>point - The point of the drop - append, above or below</li>
31162         * <li>source - The drag source</li>
31163         * <li>rawEvent - Raw mouse event</li>
31164         * <li>dropNode - Drop node(s) provided by the source.</li>
31165         * <li>cancel - Set this to true to signal drop not allowed.</li>
31166         * </ul>
31167         * @param {Object} dragOverEvent
31168         */
31169        "nodedragover" : true
31170         
31171     });
31172     if(this.singleExpand){
31173        this.on("beforeexpand", this.restrictExpand, this);
31174     }
31175     if (this.editor) {
31176         this.editor.tree = this;
31177         this.editor = Roo.factory(this.editor, Roo.tree);
31178     }
31179     
31180     if (this.selModel) {
31181         this.selModel = Roo.factory(this.selModel, Roo.tree);
31182     }
31183    
31184 };
31185 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31186     rootVisible : true,
31187     animate: Roo.enableFx,
31188     lines : true,
31189     enableDD : false,
31190     hlDrop : Roo.enableFx,
31191   
31192     renderer: false,
31193     
31194     rendererTip: false,
31195     // private
31196     restrictExpand : function(node){
31197         var p = node.parentNode;
31198         if(p){
31199             if(p.expandedChild && p.expandedChild.parentNode == p){
31200                 p.expandedChild.collapse();
31201             }
31202             p.expandedChild = node;
31203         }
31204     },
31205
31206     // private override
31207     setRootNode : function(node){
31208         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31209         if(!this.rootVisible){
31210             node.ui = new Roo.tree.RootTreeNodeUI(node);
31211         }
31212         return node;
31213     },
31214
31215     /**
31216      * Returns the container element for this TreePanel
31217      */
31218     getEl : function(){
31219         return this.el;
31220     },
31221
31222     /**
31223      * Returns the default TreeLoader for this TreePanel
31224      */
31225     getLoader : function(){
31226         return this.loader;
31227     },
31228
31229     /**
31230      * Expand all nodes
31231      */
31232     expandAll : function(){
31233         this.root.expand(true);
31234     },
31235
31236     /**
31237      * Collapse all nodes
31238      */
31239     collapseAll : function(){
31240         this.root.collapse(true);
31241     },
31242
31243     /**
31244      * Returns the selection model used by this TreePanel
31245      */
31246     getSelectionModel : function(){
31247         if(!this.selModel){
31248             this.selModel = new Roo.tree.DefaultSelectionModel();
31249         }
31250         return this.selModel;
31251     },
31252
31253     /**
31254      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31255      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31256      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31257      * @return {Array}
31258      */
31259     getChecked : function(a, startNode){
31260         startNode = startNode || this.root;
31261         var r = [];
31262         var f = function(){
31263             if(this.attributes.checked){
31264                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31265             }
31266         }
31267         startNode.cascade(f);
31268         return r;
31269     },
31270
31271     /**
31272      * Expands a specified path in this TreePanel. 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 expand is complete. The callback will be called with
31276      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31277      */
31278     expandPath : function(path, attr, callback){
31279         attr = attr || "id";
31280         var keys = path.split(this.pathSeparator);
31281         var curNode = this.root;
31282         if(curNode.attributes[attr] != keys[1]){ // invalid root
31283             if(callback){
31284                 callback(false, null);
31285             }
31286             return;
31287         }
31288         var index = 1;
31289         var f = function(){
31290             if(++index == keys.length){
31291                 if(callback){
31292                     callback(true, curNode);
31293                 }
31294                 return;
31295             }
31296             var c = curNode.findChild(attr, keys[index]);
31297             if(!c){
31298                 if(callback){
31299                     callback(false, curNode);
31300                 }
31301                 return;
31302             }
31303             curNode = c;
31304             c.expand(false, false, f);
31305         };
31306         curNode.expand(false, false, f);
31307     },
31308
31309     /**
31310      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31311      * @param {String} path
31312      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31313      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31314      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31315      */
31316     selectPath : function(path, attr, callback){
31317         attr = attr || "id";
31318         var keys = path.split(this.pathSeparator);
31319         var v = keys.pop();
31320         if(keys.length > 0){
31321             var f = function(success, node){
31322                 if(success && node){
31323                     var n = node.findChild(attr, v);
31324                     if(n){
31325                         n.select();
31326                         if(callback){
31327                             callback(true, n);
31328                         }
31329                     }else if(callback){
31330                         callback(false, n);
31331                     }
31332                 }else{
31333                     if(callback){
31334                         callback(false, n);
31335                     }
31336                 }
31337             };
31338             this.expandPath(keys.join(this.pathSeparator), attr, f);
31339         }else{
31340             this.root.select();
31341             if(callback){
31342                 callback(true, this.root);
31343             }
31344         }
31345     },
31346
31347     getTreeEl : function(){
31348         return this.el;
31349     },
31350
31351     /**
31352      * Trigger rendering of this TreePanel
31353      */
31354     render : function(){
31355         if (this.innerCt) {
31356             return this; // stop it rendering more than once!!
31357         }
31358         
31359         this.innerCt = this.el.createChild({tag:"ul",
31360                cls:"x-tree-root-ct " +
31361                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31362
31363         if(this.containerScroll){
31364             Roo.dd.ScrollManager.register(this.el);
31365         }
31366         if((this.enableDD || this.enableDrop) && !this.dropZone){
31367            /**
31368             * The dropZone used by this tree if drop is enabled
31369             * @type Roo.tree.TreeDropZone
31370             */
31371              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31372                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31373            });
31374         }
31375         if((this.enableDD || this.enableDrag) && !this.dragZone){
31376            /**
31377             * The dragZone used by this tree if drag is enabled
31378             * @type Roo.tree.TreeDragZone
31379             */
31380             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31381                ddGroup: this.ddGroup || "TreeDD",
31382                scroll: this.ddScroll
31383            });
31384         }
31385         this.getSelectionModel().init(this);
31386         if (!this.root) {
31387             console.log("ROOT not set in tree");
31388             return;
31389         }
31390         this.root.render();
31391         if(!this.rootVisible){
31392             this.root.renderChildren();
31393         }
31394         return this;
31395     }
31396 });/*
31397  * Based on:
31398  * Ext JS Library 1.1.1
31399  * Copyright(c) 2006-2007, Ext JS, LLC.
31400  *
31401  * Originally Released Under LGPL - original licence link has changed is not relivant.
31402  *
31403  * Fork - LGPL
31404  * <script type="text/javascript">
31405  */
31406  
31407
31408 /**
31409  * @class Roo.tree.DefaultSelectionModel
31410  * @extends Roo.util.Observable
31411  * The default single selection for a TreePanel.
31412  * @param {Object} cfg Configuration
31413  */
31414 Roo.tree.DefaultSelectionModel = function(cfg){
31415    this.selNode = null;
31416    
31417    
31418    
31419    this.addEvents({
31420        /**
31421         * @event selectionchange
31422         * Fires when the selected node changes
31423         * @param {DefaultSelectionModel} this
31424         * @param {TreeNode} node the new selection
31425         */
31426        "selectionchange" : true,
31427
31428        /**
31429         * @event beforeselect
31430         * Fires before the selected node changes, return false to cancel the change
31431         * @param {DefaultSelectionModel} this
31432         * @param {TreeNode} node the new selection
31433         * @param {TreeNode} node the old selection
31434         */
31435        "beforeselect" : true
31436    });
31437    
31438     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31439 };
31440
31441 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31442     init : function(tree){
31443         this.tree = tree;
31444         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31445         tree.on("click", this.onNodeClick, this);
31446     },
31447     
31448     onNodeClick : function(node, e){
31449         if (e.ctrlKey && this.selNode == node)  {
31450             this.unselect(node);
31451             return;
31452         }
31453         this.select(node);
31454     },
31455     
31456     /**
31457      * Select a node.
31458      * @param {TreeNode} node The node to select
31459      * @return {TreeNode} The selected node
31460      */
31461     select : function(node){
31462         var last = this.selNode;
31463         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31464             if(last){
31465                 last.ui.onSelectedChange(false);
31466             }
31467             this.selNode = node;
31468             node.ui.onSelectedChange(true);
31469             this.fireEvent("selectionchange", this, node, last);
31470         }
31471         return node;
31472     },
31473     
31474     /**
31475      * Deselect a node.
31476      * @param {TreeNode} node The node to unselect
31477      */
31478     unselect : function(node){
31479         if(this.selNode == node){
31480             this.clearSelections();
31481         }    
31482     },
31483     
31484     /**
31485      * Clear all selections
31486      */
31487     clearSelections : function(){
31488         var n = this.selNode;
31489         if(n){
31490             n.ui.onSelectedChange(false);
31491             this.selNode = null;
31492             this.fireEvent("selectionchange", this, null);
31493         }
31494         return n;
31495     },
31496     
31497     /**
31498      * Get the selected node
31499      * @return {TreeNode} The selected node
31500      */
31501     getSelectedNode : function(){
31502         return this.selNode;    
31503     },
31504     
31505     /**
31506      * Returns true if the node is selected
31507      * @param {TreeNode} node The node to check
31508      * @return {Boolean}
31509      */
31510     isSelected : function(node){
31511         return this.selNode == node;  
31512     },
31513
31514     /**
31515      * Selects the node above the selected node in the tree, intelligently walking the nodes
31516      * @return TreeNode The new selection
31517      */
31518     selectPrevious : function(){
31519         var s = this.selNode || this.lastSelNode;
31520         if(!s){
31521             return null;
31522         }
31523         var ps = s.previousSibling;
31524         if(ps){
31525             if(!ps.isExpanded() || ps.childNodes.length < 1){
31526                 return this.select(ps);
31527             } else{
31528                 var lc = ps.lastChild;
31529                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31530                     lc = lc.lastChild;
31531                 }
31532                 return this.select(lc);
31533             }
31534         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31535             return this.select(s.parentNode);
31536         }
31537         return null;
31538     },
31539
31540     /**
31541      * Selects the node above the selected node in the tree, intelligently walking the nodes
31542      * @return TreeNode The new selection
31543      */
31544     selectNext : function(){
31545         var s = this.selNode || this.lastSelNode;
31546         if(!s){
31547             return null;
31548         }
31549         if(s.firstChild && s.isExpanded()){
31550              return this.select(s.firstChild);
31551          }else if(s.nextSibling){
31552              return this.select(s.nextSibling);
31553          }else if(s.parentNode){
31554             var newS = null;
31555             s.parentNode.bubble(function(){
31556                 if(this.nextSibling){
31557                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31558                     return false;
31559                 }
31560             });
31561             return newS;
31562          }
31563         return null;
31564     },
31565
31566     onKeyDown : function(e){
31567         var s = this.selNode || this.lastSelNode;
31568         // undesirable, but required
31569         var sm = this;
31570         if(!s){
31571             return;
31572         }
31573         var k = e.getKey();
31574         switch(k){
31575              case e.DOWN:
31576                  e.stopEvent();
31577                  this.selectNext();
31578              break;
31579              case e.UP:
31580                  e.stopEvent();
31581                  this.selectPrevious();
31582              break;
31583              case e.RIGHT:
31584                  e.preventDefault();
31585                  if(s.hasChildNodes()){
31586                      if(!s.isExpanded()){
31587                          s.expand();
31588                      }else if(s.firstChild){
31589                          this.select(s.firstChild, e);
31590                      }
31591                  }
31592              break;
31593              case e.LEFT:
31594                  e.preventDefault();
31595                  if(s.hasChildNodes() && s.isExpanded()){
31596                      s.collapse();
31597                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31598                      this.select(s.parentNode, e);
31599                  }
31600              break;
31601         };
31602     }
31603 });
31604
31605 /**
31606  * @class Roo.tree.MultiSelectionModel
31607  * @extends Roo.util.Observable
31608  * Multi selection for a TreePanel.
31609  * @param {Object} cfg Configuration
31610  */
31611 Roo.tree.MultiSelectionModel = function(){
31612    this.selNodes = [];
31613    this.selMap = {};
31614    this.addEvents({
31615        /**
31616         * @event selectionchange
31617         * Fires when the selected nodes change
31618         * @param {MultiSelectionModel} this
31619         * @param {Array} nodes Array of the selected nodes
31620         */
31621        "selectionchange" : true
31622    });
31623    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31624    
31625 };
31626
31627 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31628     init : function(tree){
31629         this.tree = tree;
31630         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31631         tree.on("click", this.onNodeClick, this);
31632     },
31633     
31634     onNodeClick : function(node, e){
31635         this.select(node, e, e.ctrlKey);
31636     },
31637     
31638     /**
31639      * Select a node.
31640      * @param {TreeNode} node The node to select
31641      * @param {EventObject} e (optional) An event associated with the selection
31642      * @param {Boolean} keepExisting True to retain existing selections
31643      * @return {TreeNode} The selected node
31644      */
31645     select : function(node, e, keepExisting){
31646         if(keepExisting !== true){
31647             this.clearSelections(true);
31648         }
31649         if(this.isSelected(node)){
31650             this.lastSelNode = node;
31651             return node;
31652         }
31653         this.selNodes.push(node);
31654         this.selMap[node.id] = node;
31655         this.lastSelNode = node;
31656         node.ui.onSelectedChange(true);
31657         this.fireEvent("selectionchange", this, this.selNodes);
31658         return node;
31659     },
31660     
31661     /**
31662      * Deselect a node.
31663      * @param {TreeNode} node The node to unselect
31664      */
31665     unselect : function(node){
31666         if(this.selMap[node.id]){
31667             node.ui.onSelectedChange(false);
31668             var sn = this.selNodes;
31669             var index = -1;
31670             if(sn.indexOf){
31671                 index = sn.indexOf(node);
31672             }else{
31673                 for(var i = 0, len = sn.length; i < len; i++){
31674                     if(sn[i] == node){
31675                         index = i;
31676                         break;
31677                     }
31678                 }
31679             }
31680             if(index != -1){
31681                 this.selNodes.splice(index, 1);
31682             }
31683             delete this.selMap[node.id];
31684             this.fireEvent("selectionchange", this, this.selNodes);
31685         }
31686     },
31687     
31688     /**
31689      * Clear all selections
31690      */
31691     clearSelections : function(suppressEvent){
31692         var sn = this.selNodes;
31693         if(sn.length > 0){
31694             for(var i = 0, len = sn.length; i < len; i++){
31695                 sn[i].ui.onSelectedChange(false);
31696             }
31697             this.selNodes = [];
31698             this.selMap = {};
31699             if(suppressEvent !== true){
31700                 this.fireEvent("selectionchange", this, this.selNodes);
31701             }
31702         }
31703     },
31704     
31705     /**
31706      * Returns true if the node is selected
31707      * @param {TreeNode} node The node to check
31708      * @return {Boolean}
31709      */
31710     isSelected : function(node){
31711         return this.selMap[node.id] ? true : false;  
31712     },
31713     
31714     /**
31715      * Returns an array of the selected nodes
31716      * @return {Array}
31717      */
31718     getSelectedNodes : function(){
31719         return this.selNodes;    
31720     },
31721
31722     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31723
31724     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31725
31726     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31727 });/*
31728  * Based on:
31729  * Ext JS Library 1.1.1
31730  * Copyright(c) 2006-2007, Ext JS, LLC.
31731  *
31732  * Originally Released Under LGPL - original licence link has changed is not relivant.
31733  *
31734  * Fork - LGPL
31735  * <script type="text/javascript">
31736  */
31737  
31738 /**
31739  * @class Roo.tree.TreeNode
31740  * @extends Roo.data.Node
31741  * @cfg {String} text The text for this node
31742  * @cfg {Boolean} expanded true to start the node expanded
31743  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31744  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31745  * @cfg {Boolean} disabled true to start the node disabled
31746  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31747  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31748  * @cfg {String} cls A css class to be added to the node
31749  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31750  * @cfg {String} href URL of the link used for the node (defaults to #)
31751  * @cfg {String} hrefTarget target frame for the link
31752  * @cfg {String} qtip An Ext QuickTip for the node
31753  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31754  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31755  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31756  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31757  * (defaults to undefined with no checkbox rendered)
31758  * @constructor
31759  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31760  */
31761 Roo.tree.TreeNode = function(attributes){
31762     attributes = attributes || {};
31763     if(typeof attributes == "string"){
31764         attributes = {text: attributes};
31765     }
31766     this.childrenRendered = false;
31767     this.rendered = false;
31768     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31769     this.expanded = attributes.expanded === true;
31770     this.isTarget = attributes.isTarget !== false;
31771     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31772     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31773
31774     /**
31775      * Read-only. The text for this node. To change it use setText().
31776      * @type String
31777      */
31778     this.text = attributes.text;
31779     /**
31780      * True if this node is disabled.
31781      * @type Boolean
31782      */
31783     this.disabled = attributes.disabled === true;
31784
31785     this.addEvents({
31786         /**
31787         * @event textchange
31788         * Fires when the text for this node is changed
31789         * @param {Node} this This node
31790         * @param {String} text The new text
31791         * @param {String} oldText The old text
31792         */
31793         "textchange" : true,
31794         /**
31795         * @event beforeexpand
31796         * Fires before this node is expanded, return false to cancel.
31797         * @param {Node} this This node
31798         * @param {Boolean} deep
31799         * @param {Boolean} anim
31800         */
31801         "beforeexpand" : true,
31802         /**
31803         * @event beforecollapse
31804         * Fires before this node is collapsed, return false to cancel.
31805         * @param {Node} this This node
31806         * @param {Boolean} deep
31807         * @param {Boolean} anim
31808         */
31809         "beforecollapse" : true,
31810         /**
31811         * @event expand
31812         * Fires when this node is expanded
31813         * @param {Node} this This node
31814         */
31815         "expand" : true,
31816         /**
31817         * @event disabledchange
31818         * Fires when the disabled status of this node changes
31819         * @param {Node} this This node
31820         * @param {Boolean} disabled
31821         */
31822         "disabledchange" : true,
31823         /**
31824         * @event collapse
31825         * Fires when this node is collapsed
31826         * @param {Node} this This node
31827         */
31828         "collapse" : true,
31829         /**
31830         * @event beforeclick
31831         * Fires before click processing. Return false to cancel the default action.
31832         * @param {Node} this This node
31833         * @param {Roo.EventObject} e The event object
31834         */
31835         "beforeclick":true,
31836         /**
31837         * @event checkchange
31838         * Fires when a node with a checkbox's checked property changes
31839         * @param {Node} this This node
31840         * @param {Boolean} checked
31841         */
31842         "checkchange":true,
31843         /**
31844         * @event click
31845         * Fires when this node is clicked
31846         * @param {Node} this This node
31847         * @param {Roo.EventObject} e The event object
31848         */
31849         "click":true,
31850         /**
31851         * @event dblclick
31852         * Fires when this node is double clicked
31853         * @param {Node} this This node
31854         * @param {Roo.EventObject} e The event object
31855         */
31856         "dblclick":true,
31857         /**
31858         * @event contextmenu
31859         * Fires when this node is right clicked
31860         * @param {Node} this This node
31861         * @param {Roo.EventObject} e The event object
31862         */
31863         "contextmenu":true,
31864         /**
31865         * @event beforechildrenrendered
31866         * Fires right before the child nodes for this node are rendered
31867         * @param {Node} this This node
31868         */
31869         "beforechildrenrendered":true
31870     });
31871
31872     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31873
31874     /**
31875      * Read-only. The UI for this node
31876      * @type TreeNodeUI
31877      */
31878     this.ui = new uiClass(this);
31879     
31880     // finally support items[]
31881     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
31882         return;
31883     }
31884     
31885     
31886     Roo.each(this.attributes.items, function(c) {
31887         this.appendChild(Roo.factory(c,Roo.Tree));
31888     }, this);
31889     delete this.attributes.items;
31890     
31891     
31892     
31893 };
31894 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31895     preventHScroll: true,
31896     /**
31897      * Returns true if this node is expanded
31898      * @return {Boolean}
31899      */
31900     isExpanded : function(){
31901         return this.expanded;
31902     },
31903
31904     /**
31905      * Returns the UI object for this node
31906      * @return {TreeNodeUI}
31907      */
31908     getUI : function(){
31909         return this.ui;
31910     },
31911
31912     // private override
31913     setFirstChild : function(node){
31914         var of = this.firstChild;
31915         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31916         if(this.childrenRendered && of && node != of){
31917             of.renderIndent(true, true);
31918         }
31919         if(this.rendered){
31920             this.renderIndent(true, true);
31921         }
31922     },
31923
31924     // private override
31925     setLastChild : function(node){
31926         var ol = this.lastChild;
31927         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31928         if(this.childrenRendered && ol && node != ol){
31929             ol.renderIndent(true, true);
31930         }
31931         if(this.rendered){
31932             this.renderIndent(true, true);
31933         }
31934     },
31935
31936     // these methods are overridden to provide lazy rendering support
31937     // private override
31938     appendChild : function()
31939     {
31940         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31941         if(node && this.childrenRendered){
31942             node.render();
31943         }
31944         this.ui.updateExpandIcon();
31945         return node;
31946     },
31947
31948     // private override
31949     removeChild : function(node){
31950         this.ownerTree.getSelectionModel().unselect(node);
31951         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31952         // if it's been rendered remove dom node
31953         if(this.childrenRendered){
31954             node.ui.remove();
31955         }
31956         if(this.childNodes.length < 1){
31957             this.collapse(false, false);
31958         }else{
31959             this.ui.updateExpandIcon();
31960         }
31961         if(!this.firstChild) {
31962             this.childrenRendered = false;
31963         }
31964         return node;
31965     },
31966
31967     // private override
31968     insertBefore : function(node, refNode){
31969         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31970         if(newNode && refNode && this.childrenRendered){
31971             node.render();
31972         }
31973         this.ui.updateExpandIcon();
31974         return newNode;
31975     },
31976
31977     /**
31978      * Sets the text for this node
31979      * @param {String} text
31980      */
31981     setText : function(text){
31982         var oldText = this.text;
31983         this.text = text;
31984         this.attributes.text = text;
31985         if(this.rendered){ // event without subscribing
31986             this.ui.onTextChange(this, text, oldText);
31987         }
31988         this.fireEvent("textchange", this, text, oldText);
31989     },
31990
31991     /**
31992      * Triggers selection of this node
31993      */
31994     select : function(){
31995         this.getOwnerTree().getSelectionModel().select(this);
31996     },
31997
31998     /**
31999      * Triggers deselection of this node
32000      */
32001     unselect : function(){
32002         this.getOwnerTree().getSelectionModel().unselect(this);
32003     },
32004
32005     /**
32006      * Returns true if this node is selected
32007      * @return {Boolean}
32008      */
32009     isSelected : function(){
32010         return this.getOwnerTree().getSelectionModel().isSelected(this);
32011     },
32012
32013     /**
32014      * Expand this node.
32015      * @param {Boolean} deep (optional) True to expand all children as well
32016      * @param {Boolean} anim (optional) false to cancel the default animation
32017      * @param {Function} callback (optional) A callback to be called when
32018      * expanding this node completes (does not wait for deep expand to complete).
32019      * Called with 1 parameter, this node.
32020      */
32021     expand : function(deep, anim, callback){
32022         if(!this.expanded){
32023             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32024                 return;
32025             }
32026             if(!this.childrenRendered){
32027                 this.renderChildren();
32028             }
32029             this.expanded = true;
32030             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32031                 this.ui.animExpand(function(){
32032                     this.fireEvent("expand", this);
32033                     if(typeof callback == "function"){
32034                         callback(this);
32035                     }
32036                     if(deep === true){
32037                         this.expandChildNodes(true);
32038                     }
32039                 }.createDelegate(this));
32040                 return;
32041             }else{
32042                 this.ui.expand();
32043                 this.fireEvent("expand", this);
32044                 if(typeof callback == "function"){
32045                     callback(this);
32046                 }
32047             }
32048         }else{
32049            if(typeof callback == "function"){
32050                callback(this);
32051            }
32052         }
32053         if(deep === true){
32054             this.expandChildNodes(true);
32055         }
32056     },
32057
32058     isHiddenRoot : function(){
32059         return this.isRoot && !this.getOwnerTree().rootVisible;
32060     },
32061
32062     /**
32063      * Collapse this node.
32064      * @param {Boolean} deep (optional) True to collapse all children as well
32065      * @param {Boolean} anim (optional) false to cancel the default animation
32066      */
32067     collapse : function(deep, anim){
32068         if(this.expanded && !this.isHiddenRoot()){
32069             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32070                 return;
32071             }
32072             this.expanded = false;
32073             if((this.getOwnerTree().animate && anim !== false) || anim){
32074                 this.ui.animCollapse(function(){
32075                     this.fireEvent("collapse", this);
32076                     if(deep === true){
32077                         this.collapseChildNodes(true);
32078                     }
32079                 }.createDelegate(this));
32080                 return;
32081             }else{
32082                 this.ui.collapse();
32083                 this.fireEvent("collapse", this);
32084             }
32085         }
32086         if(deep === true){
32087             var cs = this.childNodes;
32088             for(var i = 0, len = cs.length; i < len; i++) {
32089                 cs[i].collapse(true, false);
32090             }
32091         }
32092     },
32093
32094     // private
32095     delayedExpand : function(delay){
32096         if(!this.expandProcId){
32097             this.expandProcId = this.expand.defer(delay, this);
32098         }
32099     },
32100
32101     // private
32102     cancelExpand : function(){
32103         if(this.expandProcId){
32104             clearTimeout(this.expandProcId);
32105         }
32106         this.expandProcId = false;
32107     },
32108
32109     /**
32110      * Toggles expanded/collapsed state of the node
32111      */
32112     toggle : function(){
32113         if(this.expanded){
32114             this.collapse();
32115         }else{
32116             this.expand();
32117         }
32118     },
32119
32120     /**
32121      * Ensures all parent nodes are expanded
32122      */
32123     ensureVisible : function(callback){
32124         var tree = this.getOwnerTree();
32125         tree.expandPath(this.parentNode.getPath(), false, function(){
32126             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32127             Roo.callback(callback);
32128         }.createDelegate(this));
32129     },
32130
32131     /**
32132      * Expand all child nodes
32133      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32134      */
32135     expandChildNodes : function(deep){
32136         var cs = this.childNodes;
32137         for(var i = 0, len = cs.length; i < len; i++) {
32138                 cs[i].expand(deep);
32139         }
32140     },
32141
32142     /**
32143      * Collapse all child nodes
32144      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32145      */
32146     collapseChildNodes : function(deep){
32147         var cs = this.childNodes;
32148         for(var i = 0, len = cs.length; i < len; i++) {
32149                 cs[i].collapse(deep);
32150         }
32151     },
32152
32153     /**
32154      * Disables this node
32155      */
32156     disable : function(){
32157         this.disabled = true;
32158         this.unselect();
32159         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32160             this.ui.onDisableChange(this, true);
32161         }
32162         this.fireEvent("disabledchange", this, true);
32163     },
32164
32165     /**
32166      * Enables this node
32167      */
32168     enable : function(){
32169         this.disabled = false;
32170         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32171             this.ui.onDisableChange(this, false);
32172         }
32173         this.fireEvent("disabledchange", this, false);
32174     },
32175
32176     // private
32177     renderChildren : function(suppressEvent){
32178         if(suppressEvent !== false){
32179             this.fireEvent("beforechildrenrendered", this);
32180         }
32181         var cs = this.childNodes;
32182         for(var i = 0, len = cs.length; i < len; i++){
32183             cs[i].render(true);
32184         }
32185         this.childrenRendered = true;
32186     },
32187
32188     // private
32189     sort : function(fn, scope){
32190         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32191         if(this.childrenRendered){
32192             var cs = this.childNodes;
32193             for(var i = 0, len = cs.length; i < len; i++){
32194                 cs[i].render(true);
32195             }
32196         }
32197     },
32198
32199     // private
32200     render : function(bulkRender){
32201         this.ui.render(bulkRender);
32202         if(!this.rendered){
32203             this.rendered = true;
32204             if(this.expanded){
32205                 this.expanded = false;
32206                 this.expand(false, false);
32207             }
32208         }
32209     },
32210
32211     // private
32212     renderIndent : function(deep, refresh){
32213         if(refresh){
32214             this.ui.childIndent = null;
32215         }
32216         this.ui.renderIndent();
32217         if(deep === true && this.childrenRendered){
32218             var cs = this.childNodes;
32219             for(var i = 0, len = cs.length; i < len; i++){
32220                 cs[i].renderIndent(true, refresh);
32221             }
32222         }
32223     }
32224 });/*
32225  * Based on:
32226  * Ext JS Library 1.1.1
32227  * Copyright(c) 2006-2007, Ext JS, LLC.
32228  *
32229  * Originally Released Under LGPL - original licence link has changed is not relivant.
32230  *
32231  * Fork - LGPL
32232  * <script type="text/javascript">
32233  */
32234  
32235 /**
32236  * @class Roo.tree.AsyncTreeNode
32237  * @extends Roo.tree.TreeNode
32238  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32239  * @constructor
32240  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32241  */
32242  Roo.tree.AsyncTreeNode = function(config){
32243     this.loaded = false;
32244     this.loading = false;
32245     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32246     /**
32247     * @event beforeload
32248     * Fires before this node is loaded, return false to cancel
32249     * @param {Node} this This node
32250     */
32251     this.addEvents({'beforeload':true, 'load': true});
32252     /**
32253     * @event load
32254     * Fires when this node is loaded
32255     * @param {Node} this This node
32256     */
32257     /**
32258      * The loader used by this node (defaults to using the tree's defined loader)
32259      * @type TreeLoader
32260      * @property loader
32261      */
32262 };
32263 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32264     expand : function(deep, anim, callback){
32265         if(this.loading){ // if an async load is already running, waiting til it's done
32266             var timer;
32267             var f = function(){
32268                 if(!this.loading){ // done loading
32269                     clearInterval(timer);
32270                     this.expand(deep, anim, callback);
32271                 }
32272             }.createDelegate(this);
32273             timer = setInterval(f, 200);
32274             return;
32275         }
32276         if(!this.loaded){
32277             if(this.fireEvent("beforeload", this) === false){
32278                 return;
32279             }
32280             this.loading = true;
32281             this.ui.beforeLoad(this);
32282             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32283             if(loader){
32284                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32285                 return;
32286             }
32287         }
32288         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32289     },
32290     
32291     /**
32292      * Returns true if this node is currently loading
32293      * @return {Boolean}
32294      */
32295     isLoading : function(){
32296         return this.loading;  
32297     },
32298     
32299     loadComplete : function(deep, anim, callback){
32300         this.loading = false;
32301         this.loaded = true;
32302         this.ui.afterLoad(this);
32303         this.fireEvent("load", this);
32304         this.expand(deep, anim, callback);
32305     },
32306     
32307     /**
32308      * Returns true if this node has been loaded
32309      * @return {Boolean}
32310      */
32311     isLoaded : function(){
32312         return this.loaded;
32313     },
32314     
32315     hasChildNodes : function(){
32316         if(!this.isLeaf() && !this.loaded){
32317             return true;
32318         }else{
32319             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32320         }
32321     },
32322
32323     /**
32324      * Trigger a reload for this node
32325      * @param {Function} callback
32326      */
32327     reload : function(callback){
32328         this.collapse(false, false);
32329         while(this.firstChild){
32330             this.removeChild(this.firstChild);
32331         }
32332         this.childrenRendered = false;
32333         this.loaded = false;
32334         if(this.isHiddenRoot()){
32335             this.expanded = false;
32336         }
32337         this.expand(false, false, callback);
32338     }
32339 });/*
32340  * Based on:
32341  * Ext JS Library 1.1.1
32342  * Copyright(c) 2006-2007, Ext JS, LLC.
32343  *
32344  * Originally Released Under LGPL - original licence link has changed is not relivant.
32345  *
32346  * Fork - LGPL
32347  * <script type="text/javascript">
32348  */
32349  
32350 /**
32351  * @class Roo.tree.TreeNodeUI
32352  * @constructor
32353  * @param {Object} node The node to render
32354  * The TreeNode UI implementation is separate from the
32355  * tree implementation. Unless you are customizing the tree UI,
32356  * you should never have to use this directly.
32357  */
32358 Roo.tree.TreeNodeUI = function(node){
32359     this.node = node;
32360     this.rendered = false;
32361     this.animating = false;
32362     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32363 };
32364
32365 Roo.tree.TreeNodeUI.prototype = {
32366     removeChild : function(node){
32367         if(this.rendered){
32368             this.ctNode.removeChild(node.ui.getEl());
32369         }
32370     },
32371
32372     beforeLoad : function(){
32373          this.addClass("x-tree-node-loading");
32374     },
32375
32376     afterLoad : function(){
32377          this.removeClass("x-tree-node-loading");
32378     },
32379
32380     onTextChange : function(node, text, oldText){
32381         if(this.rendered){
32382             this.textNode.innerHTML = text;
32383         }
32384     },
32385
32386     onDisableChange : function(node, state){
32387         this.disabled = state;
32388         if(state){
32389             this.addClass("x-tree-node-disabled");
32390         }else{
32391             this.removeClass("x-tree-node-disabled");
32392         }
32393     },
32394
32395     onSelectedChange : function(state){
32396         if(state){
32397             this.focus();
32398             this.addClass("x-tree-selected");
32399         }else{
32400             //this.blur();
32401             this.removeClass("x-tree-selected");
32402         }
32403     },
32404
32405     onMove : function(tree, node, oldParent, newParent, index, refNode){
32406         this.childIndent = null;
32407         if(this.rendered){
32408             var targetNode = newParent.ui.getContainer();
32409             if(!targetNode){//target not rendered
32410                 this.holder = document.createElement("div");
32411                 this.holder.appendChild(this.wrap);
32412                 return;
32413             }
32414             var insertBefore = refNode ? refNode.ui.getEl() : null;
32415             if(insertBefore){
32416                 targetNode.insertBefore(this.wrap, insertBefore);
32417             }else{
32418                 targetNode.appendChild(this.wrap);
32419             }
32420             this.node.renderIndent(true);
32421         }
32422     },
32423
32424     addClass : function(cls){
32425         if(this.elNode){
32426             Roo.fly(this.elNode).addClass(cls);
32427         }
32428     },
32429
32430     removeClass : function(cls){
32431         if(this.elNode){
32432             Roo.fly(this.elNode).removeClass(cls);
32433         }
32434     },
32435
32436     remove : function(){
32437         if(this.rendered){
32438             this.holder = document.createElement("div");
32439             this.holder.appendChild(this.wrap);
32440         }
32441     },
32442
32443     fireEvent : function(){
32444         return this.node.fireEvent.apply(this.node, arguments);
32445     },
32446
32447     initEvents : function(){
32448         this.node.on("move", this.onMove, this);
32449         var E = Roo.EventManager;
32450         var a = this.anchor;
32451
32452         var el = Roo.fly(a, '_treeui');
32453
32454         if(Roo.isOpera){ // opera render bug ignores the CSS
32455             el.setStyle("text-decoration", "none");
32456         }
32457
32458         el.on("click", this.onClick, this);
32459         el.on("dblclick", this.onDblClick, this);
32460
32461         if(this.checkbox){
32462             Roo.EventManager.on(this.checkbox,
32463                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32464         }
32465
32466         el.on("contextmenu", this.onContextMenu, this);
32467
32468         var icon = Roo.fly(this.iconNode);
32469         icon.on("click", this.onClick, this);
32470         icon.on("dblclick", this.onDblClick, this);
32471         icon.on("contextmenu", this.onContextMenu, this);
32472         E.on(this.ecNode, "click", this.ecClick, this, true);
32473
32474         if(this.node.disabled){
32475             this.addClass("x-tree-node-disabled");
32476         }
32477         if(this.node.hidden){
32478             this.addClass("x-tree-node-disabled");
32479         }
32480         var ot = this.node.getOwnerTree();
32481         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32482         if(dd && (!this.node.isRoot || ot.rootVisible)){
32483             Roo.dd.Registry.register(this.elNode, {
32484                 node: this.node,
32485                 handles: this.getDDHandles(),
32486                 isHandle: false
32487             });
32488         }
32489     },
32490
32491     getDDHandles : function(){
32492         return [this.iconNode, this.textNode];
32493     },
32494
32495     hide : function(){
32496         if(this.rendered){
32497             this.wrap.style.display = "none";
32498         }
32499     },
32500
32501     show : function(){
32502         if(this.rendered){
32503             this.wrap.style.display = "";
32504         }
32505     },
32506
32507     onContextMenu : function(e){
32508         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32509             e.preventDefault();
32510             this.focus();
32511             this.fireEvent("contextmenu", this.node, e);
32512         }
32513     },
32514
32515     onClick : function(e){
32516         if(this.dropping){
32517             e.stopEvent();
32518             return;
32519         }
32520         if(this.fireEvent("beforeclick", this.node, e) !== false){
32521             if(!this.disabled && this.node.attributes.href){
32522                 this.fireEvent("click", this.node, e);
32523                 return;
32524             }
32525             e.preventDefault();
32526             if(this.disabled){
32527                 return;
32528             }
32529
32530             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32531                 this.node.toggle();
32532             }
32533
32534             this.fireEvent("click", this.node, e);
32535         }else{
32536             e.stopEvent();
32537         }
32538     },
32539
32540     onDblClick : function(e){
32541         e.preventDefault();
32542         if(this.disabled){
32543             return;
32544         }
32545         if(this.checkbox){
32546             this.toggleCheck();
32547         }
32548         if(!this.animating && this.node.hasChildNodes()){
32549             this.node.toggle();
32550         }
32551         this.fireEvent("dblclick", this.node, e);
32552     },
32553
32554     onCheckChange : function(){
32555         var checked = this.checkbox.checked;
32556         this.node.attributes.checked = checked;
32557         this.fireEvent('checkchange', this.node, checked);
32558     },
32559
32560     ecClick : function(e){
32561         if(!this.animating && this.node.hasChildNodes()){
32562             this.node.toggle();
32563         }
32564     },
32565
32566     startDrop : function(){
32567         this.dropping = true;
32568     },
32569
32570     // delayed drop so the click event doesn't get fired on a drop
32571     endDrop : function(){
32572        setTimeout(function(){
32573            this.dropping = false;
32574        }.createDelegate(this), 50);
32575     },
32576
32577     expand : function(){
32578         this.updateExpandIcon();
32579         this.ctNode.style.display = "";
32580     },
32581
32582     focus : function(){
32583         if(!this.node.preventHScroll){
32584             try{this.anchor.focus();
32585             }catch(e){}
32586         }else if(!Roo.isIE){
32587             try{
32588                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32589                 var l = noscroll.scrollLeft;
32590                 this.anchor.focus();
32591                 noscroll.scrollLeft = l;
32592             }catch(e){}
32593         }
32594     },
32595
32596     toggleCheck : function(value){
32597         var cb = this.checkbox;
32598         if(cb){
32599             cb.checked = (value === undefined ? !cb.checked : value);
32600         }
32601     },
32602
32603     blur : function(){
32604         try{
32605             this.anchor.blur();
32606         }catch(e){}
32607     },
32608
32609     animExpand : function(callback){
32610         var ct = Roo.get(this.ctNode);
32611         ct.stopFx();
32612         if(!this.node.hasChildNodes()){
32613             this.updateExpandIcon();
32614             this.ctNode.style.display = "";
32615             Roo.callback(callback);
32616             return;
32617         }
32618         this.animating = true;
32619         this.updateExpandIcon();
32620
32621         ct.slideIn('t', {
32622            callback : function(){
32623                this.animating = false;
32624                Roo.callback(callback);
32625             },
32626             scope: this,
32627             duration: this.node.ownerTree.duration || .25
32628         });
32629     },
32630
32631     highlight : function(){
32632         var tree = this.node.getOwnerTree();
32633         Roo.fly(this.wrap).highlight(
32634             tree.hlColor || "C3DAF9",
32635             {endColor: tree.hlBaseColor}
32636         );
32637     },
32638
32639     collapse : function(){
32640         this.updateExpandIcon();
32641         this.ctNode.style.display = "none";
32642     },
32643
32644     animCollapse : function(callback){
32645         var ct = Roo.get(this.ctNode);
32646         ct.enableDisplayMode('block');
32647         ct.stopFx();
32648
32649         this.animating = true;
32650         this.updateExpandIcon();
32651
32652         ct.slideOut('t', {
32653             callback : function(){
32654                this.animating = false;
32655                Roo.callback(callback);
32656             },
32657             scope: this,
32658             duration: this.node.ownerTree.duration || .25
32659         });
32660     },
32661
32662     getContainer : function(){
32663         return this.ctNode;
32664     },
32665
32666     getEl : function(){
32667         return this.wrap;
32668     },
32669
32670     appendDDGhost : function(ghostNode){
32671         ghostNode.appendChild(this.elNode.cloneNode(true));
32672     },
32673
32674     getDDRepairXY : function(){
32675         return Roo.lib.Dom.getXY(this.iconNode);
32676     },
32677
32678     onRender : function(){
32679         this.render();
32680     },
32681
32682     render : function(bulkRender){
32683         var n = this.node, a = n.attributes;
32684         var targetNode = n.parentNode ?
32685               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32686
32687         if(!this.rendered){
32688             this.rendered = true;
32689
32690             this.renderElements(n, a, targetNode, bulkRender);
32691
32692             if(a.qtip){
32693                if(this.textNode.setAttributeNS){
32694                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32695                    if(a.qtipTitle){
32696                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32697                    }
32698                }else{
32699                    this.textNode.setAttribute("ext:qtip", a.qtip);
32700                    if(a.qtipTitle){
32701                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32702                    }
32703                }
32704             }else if(a.qtipCfg){
32705                 a.qtipCfg.target = Roo.id(this.textNode);
32706                 Roo.QuickTips.register(a.qtipCfg);
32707             }
32708             this.initEvents();
32709             if(!this.node.expanded){
32710                 this.updateExpandIcon();
32711             }
32712         }else{
32713             if(bulkRender === true) {
32714                 targetNode.appendChild(this.wrap);
32715             }
32716         }
32717     },
32718
32719     renderElements : function(n, a, targetNode, bulkRender)
32720     {
32721         // add some indent caching, this helps performance when rendering a large tree
32722         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32723         var t = n.getOwnerTree();
32724         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32725         if (typeof(n.attributes.html) != 'undefined') {
32726             txt = n.attributes.html;
32727         }
32728         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32729         var cb = typeof a.checked == 'boolean';
32730         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32731         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32732             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32733             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32734             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32735             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32736             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32737              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32738                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32739             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32740             "</li>"];
32741
32742         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32743             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32744                                 n.nextSibling.ui.getEl(), buf.join(""));
32745         }else{
32746             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32747         }
32748
32749         this.elNode = this.wrap.childNodes[0];
32750         this.ctNode = this.wrap.childNodes[1];
32751         var cs = this.elNode.childNodes;
32752         this.indentNode = cs[0];
32753         this.ecNode = cs[1];
32754         this.iconNode = cs[2];
32755         var index = 3;
32756         if(cb){
32757             this.checkbox = cs[3];
32758             index++;
32759         }
32760         this.anchor = cs[index];
32761         this.textNode = cs[index].firstChild;
32762     },
32763
32764     getAnchor : function(){
32765         return this.anchor;
32766     },
32767
32768     getTextEl : function(){
32769         return this.textNode;
32770     },
32771
32772     getIconEl : function(){
32773         return this.iconNode;
32774     },
32775
32776     isChecked : function(){
32777         return this.checkbox ? this.checkbox.checked : false;
32778     },
32779
32780     updateExpandIcon : function(){
32781         if(this.rendered){
32782             var n = this.node, c1, c2;
32783             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32784             var hasChild = n.hasChildNodes();
32785             if(hasChild){
32786                 if(n.expanded){
32787                     cls += "-minus";
32788                     c1 = "x-tree-node-collapsed";
32789                     c2 = "x-tree-node-expanded";
32790                 }else{
32791                     cls += "-plus";
32792                     c1 = "x-tree-node-expanded";
32793                     c2 = "x-tree-node-collapsed";
32794                 }
32795                 if(this.wasLeaf){
32796                     this.removeClass("x-tree-node-leaf");
32797                     this.wasLeaf = false;
32798                 }
32799                 if(this.c1 != c1 || this.c2 != c2){
32800                     Roo.fly(this.elNode).replaceClass(c1, c2);
32801                     this.c1 = c1; this.c2 = c2;
32802                 }
32803             }else{
32804                 // this changes non-leafs into leafs if they have no children.
32805                 // it's not very rational behaviour..
32806                 
32807                 if(!this.wasLeaf && this.node.leaf){
32808                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32809                     delete this.c1;
32810                     delete this.c2;
32811                     this.wasLeaf = true;
32812                 }
32813             }
32814             var ecc = "x-tree-ec-icon "+cls;
32815             if(this.ecc != ecc){
32816                 this.ecNode.className = ecc;
32817                 this.ecc = ecc;
32818             }
32819         }
32820     },
32821
32822     getChildIndent : function(){
32823         if(!this.childIndent){
32824             var buf = [];
32825             var p = this.node;
32826             while(p){
32827                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32828                     if(!p.isLast()) {
32829                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32830                     } else {
32831                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32832                     }
32833                 }
32834                 p = p.parentNode;
32835             }
32836             this.childIndent = buf.join("");
32837         }
32838         return this.childIndent;
32839     },
32840
32841     renderIndent : function(){
32842         if(this.rendered){
32843             var indent = "";
32844             var p = this.node.parentNode;
32845             if(p){
32846                 indent = p.ui.getChildIndent();
32847             }
32848             if(this.indentMarkup != indent){ // don't rerender if not required
32849                 this.indentNode.innerHTML = indent;
32850                 this.indentMarkup = indent;
32851             }
32852             this.updateExpandIcon();
32853         }
32854     }
32855 };
32856
32857 Roo.tree.RootTreeNodeUI = function(){
32858     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32859 };
32860 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32861     render : function(){
32862         if(!this.rendered){
32863             var targetNode = this.node.ownerTree.innerCt.dom;
32864             this.node.expanded = true;
32865             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32866             this.wrap = this.ctNode = targetNode.firstChild;
32867         }
32868     },
32869     collapse : function(){
32870     },
32871     expand : function(){
32872     }
32873 });/*
32874  * Based on:
32875  * Ext JS Library 1.1.1
32876  * Copyright(c) 2006-2007, Ext JS, LLC.
32877  *
32878  * Originally Released Under LGPL - original licence link has changed is not relivant.
32879  *
32880  * Fork - LGPL
32881  * <script type="text/javascript">
32882  */
32883 /**
32884  * @class Roo.tree.TreeLoader
32885  * @extends Roo.util.Observable
32886  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32887  * nodes from a specified URL. The response must be a javascript Array definition
32888  * who's elements are node definition objects. eg:
32889  * <pre><code>
32890 {  success : true,
32891    data :      [
32892    
32893     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
32894     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
32895     ]
32896 }
32897
32898
32899 </code></pre>
32900  * <br><br>
32901  * The old style respose with just an array is still supported, but not recommended.
32902  * <br><br>
32903  *
32904  * A server request is sent, and child nodes are loaded only when a node is expanded.
32905  * The loading node's id is passed to the server under the parameter name "node" to
32906  * enable the server to produce the correct child nodes.
32907  * <br><br>
32908  * To pass extra parameters, an event handler may be attached to the "beforeload"
32909  * event, and the parameters specified in the TreeLoader's baseParams property:
32910  * <pre><code>
32911     myTreeLoader.on("beforeload", function(treeLoader, node) {
32912         this.baseParams.category = node.attributes.category;
32913     }, this);
32914 </code></pre><
32915  * This would pass an HTTP parameter called "category" to the server containing
32916  * the value of the Node's "category" attribute.
32917  * @constructor
32918  * Creates a new Treeloader.
32919  * @param {Object} config A config object containing config properties.
32920  */
32921 Roo.tree.TreeLoader = function(config){
32922     this.baseParams = {};
32923     this.requestMethod = "POST";
32924     Roo.apply(this, config);
32925
32926     this.addEvents({
32927     
32928         /**
32929          * @event beforeload
32930          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32931          * @param {Object} This TreeLoader object.
32932          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32933          * @param {Object} callback The callback function specified in the {@link #load} call.
32934          */
32935         beforeload : true,
32936         /**
32937          * @event load
32938          * Fires when the node has been successfuly loaded.
32939          * @param {Object} This TreeLoader object.
32940          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32941          * @param {Object} response The response object containing the data from the server.
32942          */
32943         load : true,
32944         /**
32945          * @event loadexception
32946          * Fires if the network request failed.
32947          * @param {Object} This TreeLoader object.
32948          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32949          * @param {Object} response The response object containing the data from the server.
32950          */
32951         loadexception : true,
32952         /**
32953          * @event create
32954          * Fires before a node is created, enabling you to return custom Node types 
32955          * @param {Object} This TreeLoader object.
32956          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32957          */
32958         create : true
32959     });
32960
32961     Roo.tree.TreeLoader.superclass.constructor.call(this);
32962 };
32963
32964 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32965     /**
32966     * @cfg {String} dataUrl The URL from which to request a Json string which
32967     * specifies an array of node definition object representing the child nodes
32968     * to be loaded.
32969     */
32970     /**
32971     * @cfg {Object} baseParams (optional) An object containing properties which
32972     * specify HTTP parameters to be passed to each request for child nodes.
32973     */
32974     /**
32975     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32976     * created by this loader. If the attributes sent by the server have an attribute in this object,
32977     * they take priority.
32978     */
32979     /**
32980     * @cfg {Object} uiProviders (optional) An object containing properties which
32981     * 
32982     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32983     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32984     * <i>uiProvider</i> attribute of a returned child node is a string rather
32985     * than a reference to a TreeNodeUI implementation, this that string value
32986     * is used as a property name in the uiProviders object. You can define the provider named
32987     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32988     */
32989     uiProviders : {},
32990
32991     /**
32992     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32993     * child nodes before loading.
32994     */
32995     clearOnLoad : true,
32996
32997     /**
32998     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32999     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33000     * Grid query { data : [ .....] }
33001     */
33002     
33003     root : false,
33004      /**
33005     * @cfg {String} queryParam (optional) 
33006     * Name of the query as it will be passed on the querystring (defaults to 'node')
33007     * eg. the request will be ?node=[id]
33008     */
33009     
33010     
33011     queryParam: false,
33012     
33013     /**
33014      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33015      * This is called automatically when a node is expanded, but may be used to reload
33016      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33017      * @param {Roo.tree.TreeNode} node
33018      * @param {Function} callback
33019      */
33020     load : function(node, callback){
33021         if(this.clearOnLoad){
33022             while(node.firstChild){
33023                 node.removeChild(node.firstChild);
33024             }
33025         }
33026         if(node.attributes.children){ // preloaded json children
33027             var cs = node.attributes.children;
33028             for(var i = 0, len = cs.length; i < len; i++){
33029                 node.appendChild(this.createNode(cs[i]));
33030             }
33031             if(typeof callback == "function"){
33032                 callback();
33033             }
33034         }else if(this.dataUrl){
33035             this.requestData(node, callback);
33036         }
33037     },
33038
33039     getParams: function(node){
33040         var buf = [], bp = this.baseParams;
33041         for(var key in bp){
33042             if(typeof bp[key] != "function"){
33043                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33044             }
33045         }
33046         var n = this.queryParam === false ? 'node' : this.queryParam;
33047         buf.push(n + "=", encodeURIComponent(node.id));
33048         return buf.join("");
33049     },
33050
33051     requestData : function(node, callback){
33052         if(this.fireEvent("beforeload", this, node, callback) !== false){
33053             this.transId = Roo.Ajax.request({
33054                 method:this.requestMethod,
33055                 url: this.dataUrl||this.url,
33056                 success: this.handleResponse,
33057                 failure: this.handleFailure,
33058                 scope: this,
33059                 argument: {callback: callback, node: node},
33060                 params: this.getParams(node)
33061             });
33062         }else{
33063             // if the load is cancelled, make sure we notify
33064             // the node that we are done
33065             if(typeof callback == "function"){
33066                 callback();
33067             }
33068         }
33069     },
33070
33071     isLoading : function(){
33072         return this.transId ? true : false;
33073     },
33074
33075     abort : function(){
33076         if(this.isLoading()){
33077             Roo.Ajax.abort(this.transId);
33078         }
33079     },
33080
33081     // private
33082     createNode : function(attr)
33083     {
33084         // apply baseAttrs, nice idea Corey!
33085         if(this.baseAttrs){
33086             Roo.applyIf(attr, this.baseAttrs);
33087         }
33088         if(this.applyLoader !== false){
33089             attr.loader = this;
33090         }
33091         // uiProvider = depreciated..
33092         
33093         if(typeof(attr.uiProvider) == 'string'){
33094            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33095                 /**  eval:var:attr */ eval(attr.uiProvider);
33096         }
33097         if(typeof(this.uiProviders['default']) != 'undefined') {
33098             attr.uiProvider = this.uiProviders['default'];
33099         }
33100         
33101         this.fireEvent('create', this, attr);
33102         
33103         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33104         return(attr.leaf ?
33105                         new Roo.tree.TreeNode(attr) :
33106                         new Roo.tree.AsyncTreeNode(attr));
33107     },
33108
33109     processResponse : function(response, node, callback)
33110     {
33111         var json = response.responseText;
33112         try {
33113             
33114             var o = Roo.decode(json);
33115             
33116             if (this.root === false && typeof(o.success) != undefined) {
33117                 this.root = 'data'; // the default behaviour for list like data..
33118                 }
33119                 
33120             if (this.root !== false &&  !o.success) {
33121                 // it's a failure condition.
33122                 var a = response.argument;
33123                 this.fireEvent("loadexception", this, a.node, response);
33124                 Roo.log("Load failed - should have a handler really");
33125                 return;
33126             }
33127             
33128             
33129             
33130             if (this.root !== false) {
33131                  o = o[this.root];
33132             }
33133             
33134             for(var i = 0, len = o.length; i < len; i++){
33135                 var n = this.createNode(o[i]);
33136                 if(n){
33137                     node.appendChild(n);
33138                 }
33139             }
33140             if(typeof callback == "function"){
33141                 callback(this, node);
33142             }
33143         }catch(e){
33144             this.handleFailure(response);
33145         }
33146     },
33147
33148     handleResponse : function(response){
33149         this.transId = false;
33150         var a = response.argument;
33151         this.processResponse(response, a.node, a.callback);
33152         this.fireEvent("load", this, a.node, response);
33153     },
33154
33155     handleFailure : function(response)
33156     {
33157         // should handle failure better..
33158         this.transId = false;
33159         var a = response.argument;
33160         this.fireEvent("loadexception", this, a.node, response);
33161         if(typeof a.callback == "function"){
33162             a.callback(this, a.node);
33163         }
33164     }
33165 });/*
33166  * Based on:
33167  * Ext JS Library 1.1.1
33168  * Copyright(c) 2006-2007, Ext JS, LLC.
33169  *
33170  * Originally Released Under LGPL - original licence link has changed is not relivant.
33171  *
33172  * Fork - LGPL
33173  * <script type="text/javascript">
33174  */
33175
33176 /**
33177 * @class Roo.tree.TreeFilter
33178 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33179 * @param {TreePanel} tree
33180 * @param {Object} config (optional)
33181  */
33182 Roo.tree.TreeFilter = function(tree, config){
33183     this.tree = tree;
33184     this.filtered = {};
33185     Roo.apply(this, config);
33186 };
33187
33188 Roo.tree.TreeFilter.prototype = {
33189     clearBlank:false,
33190     reverse:false,
33191     autoClear:false,
33192     remove:false,
33193
33194      /**
33195      * Filter the data by a specific attribute.
33196      * @param {String/RegExp} value Either string that the attribute value
33197      * should start with or a RegExp to test against the attribute
33198      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33199      * @param {TreeNode} startNode (optional) The node to start the filter at.
33200      */
33201     filter : function(value, attr, startNode){
33202         attr = attr || "text";
33203         var f;
33204         if(typeof value == "string"){
33205             var vlen = value.length;
33206             // auto clear empty filter
33207             if(vlen == 0 && this.clearBlank){
33208                 this.clear();
33209                 return;
33210             }
33211             value = value.toLowerCase();
33212             f = function(n){
33213                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33214             };
33215         }else if(value.exec){ // regex?
33216             f = function(n){
33217                 return value.test(n.attributes[attr]);
33218             };
33219         }else{
33220             throw 'Illegal filter type, must be string or regex';
33221         }
33222         this.filterBy(f, null, startNode);
33223         },
33224
33225     /**
33226      * Filter by a function. The passed function will be called with each
33227      * node in the tree (or from the startNode). If the function returns true, the node is kept
33228      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33229      * @param {Function} fn The filter function
33230      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33231      */
33232     filterBy : function(fn, scope, startNode){
33233         startNode = startNode || this.tree.root;
33234         if(this.autoClear){
33235             this.clear();
33236         }
33237         var af = this.filtered, rv = this.reverse;
33238         var f = function(n){
33239             if(n == startNode){
33240                 return true;
33241             }
33242             if(af[n.id]){
33243                 return false;
33244             }
33245             var m = fn.call(scope || n, n);
33246             if(!m || rv){
33247                 af[n.id] = n;
33248                 n.ui.hide();
33249                 return false;
33250             }
33251             return true;
33252         };
33253         startNode.cascade(f);
33254         if(this.remove){
33255            for(var id in af){
33256                if(typeof id != "function"){
33257                    var n = af[id];
33258                    if(n && n.parentNode){
33259                        n.parentNode.removeChild(n);
33260                    }
33261                }
33262            }
33263         }
33264     },
33265
33266     /**
33267      * Clears the current filter. Note: with the "remove" option
33268      * set a filter cannot be cleared.
33269      */
33270     clear : function(){
33271         var t = this.tree;
33272         var af = this.filtered;
33273         for(var id in af){
33274             if(typeof id != "function"){
33275                 var n = af[id];
33276                 if(n){
33277                     n.ui.show();
33278                 }
33279             }
33280         }
33281         this.filtered = {};
33282     }
33283 };
33284 /*
33285  * Based on:
33286  * Ext JS Library 1.1.1
33287  * Copyright(c) 2006-2007, Ext JS, LLC.
33288  *
33289  * Originally Released Under LGPL - original licence link has changed is not relivant.
33290  *
33291  * Fork - LGPL
33292  * <script type="text/javascript">
33293  */
33294  
33295
33296 /**
33297  * @class Roo.tree.TreeSorter
33298  * Provides sorting of nodes in a TreePanel
33299  * 
33300  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33301  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33302  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33303  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33304  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33305  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33306  * @constructor
33307  * @param {TreePanel} tree
33308  * @param {Object} config
33309  */
33310 Roo.tree.TreeSorter = function(tree, config){
33311     Roo.apply(this, config);
33312     tree.on("beforechildrenrendered", this.doSort, this);
33313     tree.on("append", this.updateSort, this);
33314     tree.on("insert", this.updateSort, this);
33315     
33316     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33317     var p = this.property || "text";
33318     var sortType = this.sortType;
33319     var fs = this.folderSort;
33320     var cs = this.caseSensitive === true;
33321     var leafAttr = this.leafAttr || 'leaf';
33322
33323     this.sortFn = function(n1, n2){
33324         if(fs){
33325             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33326                 return 1;
33327             }
33328             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33329                 return -1;
33330             }
33331         }
33332         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33333         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33334         if(v1 < v2){
33335                         return dsc ? +1 : -1;
33336                 }else if(v1 > v2){
33337                         return dsc ? -1 : +1;
33338         }else{
33339                 return 0;
33340         }
33341     };
33342 };
33343
33344 Roo.tree.TreeSorter.prototype = {
33345     doSort : function(node){
33346         node.sort(this.sortFn);
33347     },
33348     
33349     compareNodes : function(n1, n2){
33350         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33351     },
33352     
33353     updateSort : function(tree, node){
33354         if(node.childrenRendered){
33355             this.doSort.defer(1, this, [node]);
33356         }
33357     }
33358 };/*
33359  * Based on:
33360  * Ext JS Library 1.1.1
33361  * Copyright(c) 2006-2007, Ext JS, LLC.
33362  *
33363  * Originally Released Under LGPL - original licence link has changed is not relivant.
33364  *
33365  * Fork - LGPL
33366  * <script type="text/javascript">
33367  */
33368
33369 if(Roo.dd.DropZone){
33370     
33371 Roo.tree.TreeDropZone = function(tree, config){
33372     this.allowParentInsert = false;
33373     this.allowContainerDrop = false;
33374     this.appendOnly = false;
33375     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33376     this.tree = tree;
33377     this.lastInsertClass = "x-tree-no-status";
33378     this.dragOverData = {};
33379 };
33380
33381 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33382     ddGroup : "TreeDD",
33383     
33384     expandDelay : 1000,
33385     
33386     expandNode : function(node){
33387         if(node.hasChildNodes() && !node.isExpanded()){
33388             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33389         }
33390     },
33391     
33392     queueExpand : function(node){
33393         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33394     },
33395     
33396     cancelExpand : function(){
33397         if(this.expandProcId){
33398             clearTimeout(this.expandProcId);
33399             this.expandProcId = false;
33400         }
33401     },
33402     
33403     isValidDropPoint : function(n, pt, dd, e, data){
33404         if(!n || !data){ return false; }
33405         var targetNode = n.node;
33406         var dropNode = data.node;
33407         // default drop rules
33408         if(!(targetNode && targetNode.isTarget && pt)){
33409             return false;
33410         }
33411         if(pt == "append" && targetNode.allowChildren === false){
33412             return false;
33413         }
33414         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33415             return false;
33416         }
33417         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33418             return false;
33419         }
33420         // reuse the object
33421         var overEvent = this.dragOverData;
33422         overEvent.tree = this.tree;
33423         overEvent.target = targetNode;
33424         overEvent.data = data;
33425         overEvent.point = pt;
33426         overEvent.source = dd;
33427         overEvent.rawEvent = e;
33428         overEvent.dropNode = dropNode;
33429         overEvent.cancel = false;  
33430         var result = this.tree.fireEvent("nodedragover", overEvent);
33431         return overEvent.cancel === false && result !== false;
33432     },
33433     
33434     getDropPoint : function(e, n, dd){
33435         var tn = n.node;
33436         if(tn.isRoot){
33437             return tn.allowChildren !== false ? "append" : false; // always append for root
33438         }
33439         var dragEl = n.ddel;
33440         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33441         var y = Roo.lib.Event.getPageY(e);
33442         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33443         
33444         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33445         var noAppend = tn.allowChildren === false;
33446         if(this.appendOnly || tn.parentNode.allowChildren === false){
33447             return noAppend ? false : "append";
33448         }
33449         var noBelow = false;
33450         if(!this.allowParentInsert){
33451             noBelow = tn.hasChildNodes() && tn.isExpanded();
33452         }
33453         var q = (b - t) / (noAppend ? 2 : 3);
33454         if(y >= t && y < (t + q)){
33455             return "above";
33456         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33457             return "below";
33458         }else{
33459             return "append";
33460         }
33461     },
33462     
33463     onNodeEnter : function(n, dd, e, data){
33464         this.cancelExpand();
33465     },
33466     
33467     onNodeOver : function(n, dd, e, data){
33468         var pt = this.getDropPoint(e, n, dd);
33469         var node = n.node;
33470         
33471         // auto node expand check
33472         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33473             this.queueExpand(node);
33474         }else if(pt != "append"){
33475             this.cancelExpand();
33476         }
33477         
33478         // set the insert point style on the target node
33479         var returnCls = this.dropNotAllowed;
33480         if(this.isValidDropPoint(n, pt, dd, e, data)){
33481            if(pt){
33482                var el = n.ddel;
33483                var cls;
33484                if(pt == "above"){
33485                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33486                    cls = "x-tree-drag-insert-above";
33487                }else if(pt == "below"){
33488                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33489                    cls = "x-tree-drag-insert-below";
33490                }else{
33491                    returnCls = "x-tree-drop-ok-append";
33492                    cls = "x-tree-drag-append";
33493                }
33494                if(this.lastInsertClass != cls){
33495                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33496                    this.lastInsertClass = cls;
33497                }
33498            }
33499        }
33500        return returnCls;
33501     },
33502     
33503     onNodeOut : function(n, dd, e, data){
33504         this.cancelExpand();
33505         this.removeDropIndicators(n);
33506     },
33507     
33508     onNodeDrop : function(n, dd, e, data){
33509         var point = this.getDropPoint(e, n, dd);
33510         var targetNode = n.node;
33511         targetNode.ui.startDrop();
33512         if(!this.isValidDropPoint(n, point, dd, e, data)){
33513             targetNode.ui.endDrop();
33514             return false;
33515         }
33516         // first try to find the drop node
33517         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33518         var dropEvent = {
33519             tree : this.tree,
33520             target: targetNode,
33521             data: data,
33522             point: point,
33523             source: dd,
33524             rawEvent: e,
33525             dropNode: dropNode,
33526             cancel: !dropNode   
33527         };
33528         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33529         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33530             targetNode.ui.endDrop();
33531             return false;
33532         }
33533         // allow target changing
33534         targetNode = dropEvent.target;
33535         if(point == "append" && !targetNode.isExpanded()){
33536             targetNode.expand(false, null, function(){
33537                 this.completeDrop(dropEvent);
33538             }.createDelegate(this));
33539         }else{
33540             this.completeDrop(dropEvent);
33541         }
33542         return true;
33543     },
33544     
33545     completeDrop : function(de){
33546         var ns = de.dropNode, p = de.point, t = de.target;
33547         if(!(ns instanceof Array)){
33548             ns = [ns];
33549         }
33550         var n;
33551         for(var i = 0, len = ns.length; i < len; i++){
33552             n = ns[i];
33553             if(p == "above"){
33554                 t.parentNode.insertBefore(n, t);
33555             }else if(p == "below"){
33556                 t.parentNode.insertBefore(n, t.nextSibling);
33557             }else{
33558                 t.appendChild(n);
33559             }
33560         }
33561         n.ui.focus();
33562         if(this.tree.hlDrop){
33563             n.ui.highlight();
33564         }
33565         t.ui.endDrop();
33566         this.tree.fireEvent("nodedrop", de);
33567     },
33568     
33569     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33570         if(this.tree.hlDrop){
33571             dropNode.ui.focus();
33572             dropNode.ui.highlight();
33573         }
33574         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33575     },
33576     
33577     getTree : function(){
33578         return this.tree;
33579     },
33580     
33581     removeDropIndicators : function(n){
33582         if(n && n.ddel){
33583             var el = n.ddel;
33584             Roo.fly(el).removeClass([
33585                     "x-tree-drag-insert-above",
33586                     "x-tree-drag-insert-below",
33587                     "x-tree-drag-append"]);
33588             this.lastInsertClass = "_noclass";
33589         }
33590     },
33591     
33592     beforeDragDrop : function(target, e, id){
33593         this.cancelExpand();
33594         return true;
33595     },
33596     
33597     afterRepair : function(data){
33598         if(data && Roo.enableFx){
33599             data.node.ui.highlight();
33600         }
33601         this.hideProxy();
33602     }    
33603 });
33604
33605 }
33606 /*
33607  * Based on:
33608  * Ext JS Library 1.1.1
33609  * Copyright(c) 2006-2007, Ext JS, LLC.
33610  *
33611  * Originally Released Under LGPL - original licence link has changed is not relivant.
33612  *
33613  * Fork - LGPL
33614  * <script type="text/javascript">
33615  */
33616  
33617
33618 if(Roo.dd.DragZone){
33619 Roo.tree.TreeDragZone = function(tree, config){
33620     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33621     this.tree = tree;
33622 };
33623
33624 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33625     ddGroup : "TreeDD",
33626     
33627     onBeforeDrag : function(data, e){
33628         var n = data.node;
33629         return n && n.draggable && !n.disabled;
33630     },
33631     
33632     onInitDrag : function(e){
33633         var data = this.dragData;
33634         this.tree.getSelectionModel().select(data.node);
33635         this.proxy.update("");
33636         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33637         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33638     },
33639     
33640     getRepairXY : function(e, data){
33641         return data.node.ui.getDDRepairXY();
33642     },
33643     
33644     onEndDrag : function(data, e){
33645         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33646     },
33647     
33648     onValidDrop : function(dd, e, id){
33649         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33650         this.hideProxy();
33651     },
33652     
33653     beforeInvalidDrop : function(e, id){
33654         // this scrolls the original position back into view
33655         var sm = this.tree.getSelectionModel();
33656         sm.clearSelections();
33657         sm.select(this.dragData.node);
33658     }
33659 });
33660 }/*
33661  * Based on:
33662  * Ext JS Library 1.1.1
33663  * Copyright(c) 2006-2007, Ext JS, LLC.
33664  *
33665  * Originally Released Under LGPL - original licence link has changed is not relivant.
33666  *
33667  * Fork - LGPL
33668  * <script type="text/javascript">
33669  */
33670 /**
33671  * @class Roo.tree.TreeEditor
33672  * @extends Roo.Editor
33673  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33674  * as the editor field.
33675  * @constructor
33676  * @param {Object} config (used to be the tree panel.)
33677  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33678  * 
33679  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33680  * @cfg {Roo.form.TextField|Object} field The field configuration
33681  *
33682  * 
33683  */
33684 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33685     var tree = config;
33686     var field;
33687     if (oldconfig) { // old style..
33688         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33689     } else {
33690         // new style..
33691         tree = config.tree;
33692         config.field = config.field  || {};
33693         config.field.xtype = 'TextField';
33694         field = Roo.factory(config.field, Roo.form);
33695     }
33696     config = config || {};
33697     
33698     
33699     this.addEvents({
33700         /**
33701          * @event beforenodeedit
33702          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33703          * false from the handler of this event.
33704          * @param {Editor} this
33705          * @param {Roo.tree.Node} node 
33706          */
33707         "beforenodeedit" : true
33708     });
33709     
33710     //Roo.log(config);
33711     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33712
33713     this.tree = tree;
33714
33715     tree.on('beforeclick', this.beforeNodeClick, this);
33716     tree.getTreeEl().on('mousedown', this.hide, this);
33717     this.on('complete', this.updateNode, this);
33718     this.on('beforestartedit', this.fitToTree, this);
33719     this.on('startedit', this.bindScroll, this, {delay:10});
33720     this.on('specialkey', this.onSpecialKey, this);
33721 };
33722
33723 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33724     /**
33725      * @cfg {String} alignment
33726      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33727      */
33728     alignment: "l-l",
33729     // inherit
33730     autoSize: false,
33731     /**
33732      * @cfg {Boolean} hideEl
33733      * True to hide the bound element while the editor is displayed (defaults to false)
33734      */
33735     hideEl : false,
33736     /**
33737      * @cfg {String} cls
33738      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33739      */
33740     cls: "x-small-editor x-tree-editor",
33741     /**
33742      * @cfg {Boolean} shim
33743      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33744      */
33745     shim:false,
33746     // inherit
33747     shadow:"frame",
33748     /**
33749      * @cfg {Number} maxWidth
33750      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33751      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33752      * scroll and client offsets into account prior to each edit.
33753      */
33754     maxWidth: 250,
33755
33756     editDelay : 350,
33757
33758     // private
33759     fitToTree : function(ed, el){
33760         var td = this.tree.getTreeEl().dom, nd = el.dom;
33761         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33762             td.scrollLeft = nd.offsetLeft;
33763         }
33764         var w = Math.min(
33765                 this.maxWidth,
33766                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33767         this.setSize(w, '');
33768         
33769         return this.fireEvent('beforenodeedit', this, this.editNode);
33770         
33771     },
33772
33773     // private
33774     triggerEdit : function(node){
33775         this.completeEdit();
33776         this.editNode = node;
33777         this.startEdit(node.ui.textNode, node.text);
33778     },
33779
33780     // private
33781     bindScroll : function(){
33782         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33783     },
33784
33785     // private
33786     beforeNodeClick : function(node, e){
33787         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33788         this.lastClick = new Date();
33789         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33790             e.stopEvent();
33791             this.triggerEdit(node);
33792             return false;
33793         }
33794         return true;
33795     },
33796
33797     // private
33798     updateNode : function(ed, value){
33799         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33800         this.editNode.setText(value);
33801     },
33802
33803     // private
33804     onHide : function(){
33805         Roo.tree.TreeEditor.superclass.onHide.call(this);
33806         if(this.editNode){
33807             this.editNode.ui.focus();
33808         }
33809     },
33810
33811     // private
33812     onSpecialKey : function(field, e){
33813         var k = e.getKey();
33814         if(k == e.ESC){
33815             e.stopEvent();
33816             this.cancelEdit();
33817         }else if(k == e.ENTER && !e.hasModifier()){
33818             e.stopEvent();
33819             this.completeEdit();
33820         }
33821     }
33822 });//<Script type="text/javascript">
33823 /*
33824  * Based on:
33825  * Ext JS Library 1.1.1
33826  * Copyright(c) 2006-2007, Ext JS, LLC.
33827  *
33828  * Originally Released Under LGPL - original licence link has changed is not relivant.
33829  *
33830  * Fork - LGPL
33831  * <script type="text/javascript">
33832  */
33833  
33834 /**
33835  * Not documented??? - probably should be...
33836  */
33837
33838 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33839     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33840     
33841     renderElements : function(n, a, targetNode, bulkRender){
33842         //consel.log("renderElements?");
33843         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33844
33845         var t = n.getOwnerTree();
33846         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33847         
33848         var cols = t.columns;
33849         var bw = t.borderWidth;
33850         var c = cols[0];
33851         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33852          var cb = typeof a.checked == "boolean";
33853         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33854         var colcls = 'x-t-' + tid + '-c0';
33855         var buf = [
33856             '<li class="x-tree-node">',
33857             
33858                 
33859                 '<div class="x-tree-node-el ', a.cls,'">',
33860                     // extran...
33861                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33862                 
33863                 
33864                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33865                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33866                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33867                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33868                            (a.iconCls ? ' '+a.iconCls : ''),
33869                            '" unselectable="on" />',
33870                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33871                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33872                              
33873                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33874                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33875                             '<span unselectable="on" qtip="' + tx + '">',
33876                              tx,
33877                              '</span></a>' ,
33878                     '</div>',
33879                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33880                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33881                  ];
33882         for(var i = 1, len = cols.length; i < len; i++){
33883             c = cols[i];
33884             colcls = 'x-t-' + tid + '-c' +i;
33885             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33886             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33887                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33888                       "</div>");
33889          }
33890          
33891          buf.push(
33892             '</a>',
33893             '<div class="x-clear"></div></div>',
33894             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33895             "</li>");
33896         
33897         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33898             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33899                                 n.nextSibling.ui.getEl(), buf.join(""));
33900         }else{
33901             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33902         }
33903         var el = this.wrap.firstChild;
33904         this.elRow = el;
33905         this.elNode = el.firstChild;
33906         this.ranchor = el.childNodes[1];
33907         this.ctNode = this.wrap.childNodes[1];
33908         var cs = el.firstChild.childNodes;
33909         this.indentNode = cs[0];
33910         this.ecNode = cs[1];
33911         this.iconNode = cs[2];
33912         var index = 3;
33913         if(cb){
33914             this.checkbox = cs[3];
33915             index++;
33916         }
33917         this.anchor = cs[index];
33918         
33919         this.textNode = cs[index].firstChild;
33920         
33921         //el.on("click", this.onClick, this);
33922         //el.on("dblclick", this.onDblClick, this);
33923         
33924         
33925        // console.log(this);
33926     },
33927     initEvents : function(){
33928         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33929         
33930             
33931         var a = this.ranchor;
33932
33933         var el = Roo.get(a);
33934
33935         if(Roo.isOpera){ // opera render bug ignores the CSS
33936             el.setStyle("text-decoration", "none");
33937         }
33938
33939         el.on("click", this.onClick, this);
33940         el.on("dblclick", this.onDblClick, this);
33941         el.on("contextmenu", this.onContextMenu, this);
33942         
33943     },
33944     
33945     /*onSelectedChange : function(state){
33946         if(state){
33947             this.focus();
33948             this.addClass("x-tree-selected");
33949         }else{
33950             //this.blur();
33951             this.removeClass("x-tree-selected");
33952         }
33953     },*/
33954     addClass : function(cls){
33955         if(this.elRow){
33956             Roo.fly(this.elRow).addClass(cls);
33957         }
33958         
33959     },
33960     
33961     
33962     removeClass : function(cls){
33963         if(this.elRow){
33964             Roo.fly(this.elRow).removeClass(cls);
33965         }
33966     }
33967
33968     
33969     
33970 });//<Script type="text/javascript">
33971
33972 /*
33973  * Based on:
33974  * Ext JS Library 1.1.1
33975  * Copyright(c) 2006-2007, Ext JS, LLC.
33976  *
33977  * Originally Released Under LGPL - original licence link has changed is not relivant.
33978  *
33979  * Fork - LGPL
33980  * <script type="text/javascript">
33981  */
33982  
33983
33984 /**
33985  * @class Roo.tree.ColumnTree
33986  * @extends Roo.data.TreePanel
33987  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33988  * @cfg {int} borderWidth  compined right/left border allowance
33989  * @constructor
33990  * @param {String/HTMLElement/Element} el The container element
33991  * @param {Object} config
33992  */
33993 Roo.tree.ColumnTree =  function(el, config)
33994 {
33995    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33996    this.addEvents({
33997         /**
33998         * @event resize
33999         * Fire this event on a container when it resizes
34000         * @param {int} w Width
34001         * @param {int} h Height
34002         */
34003        "resize" : true
34004     });
34005     this.on('resize', this.onResize, this);
34006 };
34007
34008 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34009     //lines:false,
34010     
34011     
34012     borderWidth: Roo.isBorderBox ? 0 : 2, 
34013     headEls : false,
34014     
34015     render : function(){
34016         // add the header.....
34017        
34018         Roo.tree.ColumnTree.superclass.render.apply(this);
34019         
34020         this.el.addClass('x-column-tree');
34021         
34022         this.headers = this.el.createChild(
34023             {cls:'x-tree-headers'},this.innerCt.dom);
34024    
34025         var cols = this.columns, c;
34026         var totalWidth = 0;
34027         this.headEls = [];
34028         var  len = cols.length;
34029         for(var i = 0; i < len; i++){
34030              c = cols[i];
34031              totalWidth += c.width;
34032             this.headEls.push(this.headers.createChild({
34033                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34034                  cn: {
34035                      cls:'x-tree-hd-text',
34036                      html: c.header
34037                  },
34038                  style:'width:'+(c.width-this.borderWidth)+'px;'
34039              }));
34040         }
34041         this.headers.createChild({cls:'x-clear'});
34042         // prevent floats from wrapping when clipped
34043         this.headers.setWidth(totalWidth);
34044         //this.innerCt.setWidth(totalWidth);
34045         this.innerCt.setStyle({ overflow: 'auto' });
34046         this.onResize(this.width, this.height);
34047              
34048         
34049     },
34050     onResize : function(w,h)
34051     {
34052         this.height = h;
34053         this.width = w;
34054         // resize cols..
34055         this.innerCt.setWidth(this.width);
34056         this.innerCt.setHeight(this.height-20);
34057         
34058         // headers...
34059         var cols = this.columns, c;
34060         var totalWidth = 0;
34061         var expEl = false;
34062         var len = cols.length;
34063         for(var i = 0; i < len; i++){
34064             c = cols[i];
34065             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34066                 // it's the expander..
34067                 expEl  = this.headEls[i];
34068                 continue;
34069             }
34070             totalWidth += c.width;
34071             
34072         }
34073         if (expEl) {
34074             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34075         }
34076         this.headers.setWidth(w-20);
34077
34078         
34079         
34080         
34081     }
34082 });
34083 /*
34084  * Based on:
34085  * Ext JS Library 1.1.1
34086  * Copyright(c) 2006-2007, Ext JS, LLC.
34087  *
34088  * Originally Released Under LGPL - original licence link has changed is not relivant.
34089  *
34090  * Fork - LGPL
34091  * <script type="text/javascript">
34092  */
34093  
34094 /**
34095  * @class Roo.menu.Menu
34096  * @extends Roo.util.Observable
34097  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34098  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34099  * @constructor
34100  * Creates a new Menu
34101  * @param {Object} config Configuration options
34102  */
34103 Roo.menu.Menu = function(config){
34104     Roo.apply(this, config);
34105     this.id = this.id || Roo.id();
34106     this.addEvents({
34107         /**
34108          * @event beforeshow
34109          * Fires before this menu is displayed
34110          * @param {Roo.menu.Menu} this
34111          */
34112         beforeshow : true,
34113         /**
34114          * @event beforehide
34115          * Fires before this menu is hidden
34116          * @param {Roo.menu.Menu} this
34117          */
34118         beforehide : true,
34119         /**
34120          * @event show
34121          * Fires after this menu is displayed
34122          * @param {Roo.menu.Menu} this
34123          */
34124         show : true,
34125         /**
34126          * @event hide
34127          * Fires after this menu is hidden
34128          * @param {Roo.menu.Menu} this
34129          */
34130         hide : true,
34131         /**
34132          * @event click
34133          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34134          * @param {Roo.menu.Menu} this
34135          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34136          * @param {Roo.EventObject} e
34137          */
34138         click : true,
34139         /**
34140          * @event mouseover
34141          * Fires when the mouse is hovering over this menu
34142          * @param {Roo.menu.Menu} this
34143          * @param {Roo.EventObject} e
34144          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34145          */
34146         mouseover : true,
34147         /**
34148          * @event mouseout
34149          * Fires when the mouse exits this menu
34150          * @param {Roo.menu.Menu} this
34151          * @param {Roo.EventObject} e
34152          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34153          */
34154         mouseout : true,
34155         /**
34156          * @event itemclick
34157          * Fires when a menu item contained in this menu is clicked
34158          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34159          * @param {Roo.EventObject} e
34160          */
34161         itemclick: true
34162     });
34163     if (this.registerMenu) {
34164         Roo.menu.MenuMgr.register(this);
34165     }
34166     
34167     var mis = this.items;
34168     this.items = new Roo.util.MixedCollection();
34169     if(mis){
34170         this.add.apply(this, mis);
34171     }
34172 };
34173
34174 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34175     /**
34176      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34177      */
34178     minWidth : 120,
34179     /**
34180      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34181      * for bottom-right shadow (defaults to "sides")
34182      */
34183     shadow : "sides",
34184     /**
34185      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34186      * this menu (defaults to "tl-tr?")
34187      */
34188     subMenuAlign : "tl-tr?",
34189     /**
34190      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34191      * relative to its element of origin (defaults to "tl-bl?")
34192      */
34193     defaultAlign : "tl-bl?",
34194     /**
34195      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34196      */
34197     allowOtherMenus : false,
34198     /**
34199      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34200      */
34201     registerMenu : true,
34202
34203     hidden:true,
34204
34205     // private
34206     render : function(){
34207         if(this.el){
34208             return;
34209         }
34210         var el = this.el = new Roo.Layer({
34211             cls: "x-menu",
34212             shadow:this.shadow,
34213             constrain: false,
34214             parentEl: this.parentEl || document.body,
34215             zindex:15000
34216         });
34217
34218         this.keyNav = new Roo.menu.MenuNav(this);
34219
34220         if(this.plain){
34221             el.addClass("x-menu-plain");
34222         }
34223         if(this.cls){
34224             el.addClass(this.cls);
34225         }
34226         // generic focus element
34227         this.focusEl = el.createChild({
34228             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34229         });
34230         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34231         ul.on("click", this.onClick, this);
34232         ul.on("mouseover", this.onMouseOver, this);
34233         ul.on("mouseout", this.onMouseOut, this);
34234         this.items.each(function(item){
34235             var li = document.createElement("li");
34236             li.className = "x-menu-list-item";
34237             ul.dom.appendChild(li);
34238             item.render(li, this);
34239         }, this);
34240         this.ul = ul;
34241         this.autoWidth();
34242     },
34243
34244     // private
34245     autoWidth : function(){
34246         var el = this.el, ul = this.ul;
34247         if(!el){
34248             return;
34249         }
34250         var w = this.width;
34251         if(w){
34252             el.setWidth(w);
34253         }else if(Roo.isIE){
34254             el.setWidth(this.minWidth);
34255             var t = el.dom.offsetWidth; // force recalc
34256             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34257         }
34258     },
34259
34260     // private
34261     delayAutoWidth : function(){
34262         if(this.rendered){
34263             if(!this.awTask){
34264                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34265             }
34266             this.awTask.delay(20);
34267         }
34268     },
34269
34270     // private
34271     findTargetItem : function(e){
34272         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34273         if(t && t.menuItemId){
34274             return this.items.get(t.menuItemId);
34275         }
34276     },
34277
34278     // private
34279     onClick : function(e){
34280         var t;
34281         if(t = this.findTargetItem(e)){
34282             t.onClick(e);
34283             this.fireEvent("click", this, t, e);
34284         }
34285     },
34286
34287     // private
34288     setActiveItem : function(item, autoExpand){
34289         if(item != this.activeItem){
34290             if(this.activeItem){
34291                 this.activeItem.deactivate();
34292             }
34293             this.activeItem = item;
34294             item.activate(autoExpand);
34295         }else if(autoExpand){
34296             item.expandMenu();
34297         }
34298     },
34299
34300     // private
34301     tryActivate : function(start, step){
34302         var items = this.items;
34303         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34304             var item = items.get(i);
34305             if(!item.disabled && item.canActivate){
34306                 this.setActiveItem(item, false);
34307                 return item;
34308             }
34309         }
34310         return false;
34311     },
34312
34313     // private
34314     onMouseOver : function(e){
34315         var t;
34316         if(t = this.findTargetItem(e)){
34317             if(t.canActivate && !t.disabled){
34318                 this.setActiveItem(t, true);
34319             }
34320         }
34321         this.fireEvent("mouseover", this, e, t);
34322     },
34323
34324     // private
34325     onMouseOut : function(e){
34326         var t;
34327         if(t = this.findTargetItem(e)){
34328             if(t == this.activeItem && t.shouldDeactivate(e)){
34329                 this.activeItem.deactivate();
34330                 delete this.activeItem;
34331             }
34332         }
34333         this.fireEvent("mouseout", this, e, t);
34334     },
34335
34336     /**
34337      * Read-only.  Returns true if the menu is currently displayed, else false.
34338      * @type Boolean
34339      */
34340     isVisible : function(){
34341         return this.el && !this.hidden;
34342     },
34343
34344     /**
34345      * Displays this menu relative to another element
34346      * @param {String/HTMLElement/Roo.Element} element The element to align to
34347      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34348      * the element (defaults to this.defaultAlign)
34349      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34350      */
34351     show : function(el, pos, parentMenu){
34352         this.parentMenu = parentMenu;
34353         if(!this.el){
34354             this.render();
34355         }
34356         this.fireEvent("beforeshow", this);
34357         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34358     },
34359
34360     /**
34361      * Displays this menu at a specific xy position
34362      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34363      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34364      */
34365     showAt : function(xy, parentMenu, /* private: */_e){
34366         this.parentMenu = parentMenu;
34367         if(!this.el){
34368             this.render();
34369         }
34370         if(_e !== false){
34371             this.fireEvent("beforeshow", this);
34372             xy = this.el.adjustForConstraints(xy);
34373         }
34374         this.el.setXY(xy);
34375         this.el.show();
34376         this.hidden = false;
34377         this.focus();
34378         this.fireEvent("show", this);
34379     },
34380
34381     focus : function(){
34382         if(!this.hidden){
34383             this.doFocus.defer(50, this);
34384         }
34385     },
34386
34387     doFocus : function(){
34388         if(!this.hidden){
34389             this.focusEl.focus();
34390         }
34391     },
34392
34393     /**
34394      * Hides this menu and optionally all parent menus
34395      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34396      */
34397     hide : function(deep){
34398         if(this.el && this.isVisible()){
34399             this.fireEvent("beforehide", this);
34400             if(this.activeItem){
34401                 this.activeItem.deactivate();
34402                 this.activeItem = null;
34403             }
34404             this.el.hide();
34405             this.hidden = true;
34406             this.fireEvent("hide", this);
34407         }
34408         if(deep === true && this.parentMenu){
34409             this.parentMenu.hide(true);
34410         }
34411     },
34412
34413     /**
34414      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34415      * Any of the following are valid:
34416      * <ul>
34417      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34418      * <li>An HTMLElement object which will be converted to a menu item</li>
34419      * <li>A menu item config object that will be created as a new menu item</li>
34420      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34421      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34422      * </ul>
34423      * Usage:
34424      * <pre><code>
34425 // Create the menu
34426 var menu = new Roo.menu.Menu();
34427
34428 // Create a menu item to add by reference
34429 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34430
34431 // Add a bunch of items at once using different methods.
34432 // Only the last item added will be returned.
34433 var item = menu.add(
34434     menuItem,                // add existing item by ref
34435     'Dynamic Item',          // new TextItem
34436     '-',                     // new separator
34437     { text: 'Config Item' }  // new item by config
34438 );
34439 </code></pre>
34440      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34441      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34442      */
34443     add : function(){
34444         var a = arguments, l = a.length, item;
34445         for(var i = 0; i < l; i++){
34446             var el = a[i];
34447             if ((typeof(el) == "object") && el.xtype && el.xns) {
34448                 el = Roo.factory(el, Roo.menu);
34449             }
34450             
34451             if(el.render){ // some kind of Item
34452                 item = this.addItem(el);
34453             }else if(typeof el == "string"){ // string
34454                 if(el == "separator" || el == "-"){
34455                     item = this.addSeparator();
34456                 }else{
34457                     item = this.addText(el);
34458                 }
34459             }else if(el.tagName || el.el){ // element
34460                 item = this.addElement(el);
34461             }else if(typeof el == "object"){ // must be menu item config?
34462                 item = this.addMenuItem(el);
34463             }
34464         }
34465         return item;
34466     },
34467
34468     /**
34469      * Returns this menu's underlying {@link Roo.Element} object
34470      * @return {Roo.Element} The element
34471      */
34472     getEl : function(){
34473         if(!this.el){
34474             this.render();
34475         }
34476         return this.el;
34477     },
34478
34479     /**
34480      * Adds a separator bar to the menu
34481      * @return {Roo.menu.Item} The menu item that was added
34482      */
34483     addSeparator : function(){
34484         return this.addItem(new Roo.menu.Separator());
34485     },
34486
34487     /**
34488      * Adds an {@link Roo.Element} object to the menu
34489      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34490      * @return {Roo.menu.Item} The menu item that was added
34491      */
34492     addElement : function(el){
34493         return this.addItem(new Roo.menu.BaseItem(el));
34494     },
34495
34496     /**
34497      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34498      * @param {Roo.menu.Item} item The menu item to add
34499      * @return {Roo.menu.Item} The menu item that was added
34500      */
34501     addItem : function(item){
34502         this.items.add(item);
34503         if(this.ul){
34504             var li = document.createElement("li");
34505             li.className = "x-menu-list-item";
34506             this.ul.dom.appendChild(li);
34507             item.render(li, this);
34508             this.delayAutoWidth();
34509         }
34510         return item;
34511     },
34512
34513     /**
34514      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34515      * @param {Object} config A MenuItem config object
34516      * @return {Roo.menu.Item} The menu item that was added
34517      */
34518     addMenuItem : function(config){
34519         if(!(config instanceof Roo.menu.Item)){
34520             if(typeof config.checked == "boolean"){ // must be check menu item config?
34521                 config = new Roo.menu.CheckItem(config);
34522             }else{
34523                 config = new Roo.menu.Item(config);
34524             }
34525         }
34526         return this.addItem(config);
34527     },
34528
34529     /**
34530      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34531      * @param {String} text The text to display in the menu item
34532      * @return {Roo.menu.Item} The menu item that was added
34533      */
34534     addText : function(text){
34535         return this.addItem(new Roo.menu.TextItem({ text : text }));
34536     },
34537
34538     /**
34539      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34540      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34541      * @param {Roo.menu.Item} item The menu item to add
34542      * @return {Roo.menu.Item} The menu item that was added
34543      */
34544     insert : function(index, item){
34545         this.items.insert(index, item);
34546         if(this.ul){
34547             var li = document.createElement("li");
34548             li.className = "x-menu-list-item";
34549             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34550             item.render(li, this);
34551             this.delayAutoWidth();
34552         }
34553         return item;
34554     },
34555
34556     /**
34557      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34558      * @param {Roo.menu.Item} item The menu item to remove
34559      */
34560     remove : function(item){
34561         this.items.removeKey(item.id);
34562         item.destroy();
34563     },
34564
34565     /**
34566      * Removes and destroys all items in the menu
34567      */
34568     removeAll : function(){
34569         var f;
34570         while(f = this.items.first()){
34571             this.remove(f);
34572         }
34573     }
34574 });
34575
34576 // MenuNav is a private utility class used internally by the Menu
34577 Roo.menu.MenuNav = function(menu){
34578     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34579     this.scope = this.menu = menu;
34580 };
34581
34582 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34583     doRelay : function(e, h){
34584         var k = e.getKey();
34585         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34586             this.menu.tryActivate(0, 1);
34587             return false;
34588         }
34589         return h.call(this.scope || this, e, this.menu);
34590     },
34591
34592     up : function(e, m){
34593         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34594             m.tryActivate(m.items.length-1, -1);
34595         }
34596     },
34597
34598     down : function(e, m){
34599         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34600             m.tryActivate(0, 1);
34601         }
34602     },
34603
34604     right : function(e, m){
34605         if(m.activeItem){
34606             m.activeItem.expandMenu(true);
34607         }
34608     },
34609
34610     left : function(e, m){
34611         m.hide();
34612         if(m.parentMenu && m.parentMenu.activeItem){
34613             m.parentMenu.activeItem.activate();
34614         }
34615     },
34616
34617     enter : function(e, m){
34618         if(m.activeItem){
34619             e.stopPropagation();
34620             m.activeItem.onClick(e);
34621             m.fireEvent("click", this, m.activeItem);
34622             return true;
34623         }
34624     }
34625 });/*
34626  * Based on:
34627  * Ext JS Library 1.1.1
34628  * Copyright(c) 2006-2007, Ext JS, LLC.
34629  *
34630  * Originally Released Under LGPL - original licence link has changed is not relivant.
34631  *
34632  * Fork - LGPL
34633  * <script type="text/javascript">
34634  */
34635  
34636 /**
34637  * @class Roo.menu.MenuMgr
34638  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34639  * @singleton
34640  */
34641 Roo.menu.MenuMgr = function(){
34642    var menus, active, groups = {}, attached = false, lastShow = new Date();
34643
34644    // private - called when first menu is created
34645    function init(){
34646        menus = {};
34647        active = new Roo.util.MixedCollection();
34648        Roo.get(document).addKeyListener(27, function(){
34649            if(active.length > 0){
34650                hideAll();
34651            }
34652        });
34653    }
34654
34655    // private
34656    function hideAll(){
34657        if(active && active.length > 0){
34658            var c = active.clone();
34659            c.each(function(m){
34660                m.hide();
34661            });
34662        }
34663    }
34664
34665    // private
34666    function onHide(m){
34667        active.remove(m);
34668        if(active.length < 1){
34669            Roo.get(document).un("mousedown", onMouseDown);
34670            attached = false;
34671        }
34672    }
34673
34674    // private
34675    function onShow(m){
34676        var last = active.last();
34677        lastShow = new Date();
34678        active.add(m);
34679        if(!attached){
34680            Roo.get(document).on("mousedown", onMouseDown);
34681            attached = true;
34682        }
34683        if(m.parentMenu){
34684           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34685           m.parentMenu.activeChild = m;
34686        }else if(last && last.isVisible()){
34687           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34688        }
34689    }
34690
34691    // private
34692    function onBeforeHide(m){
34693        if(m.activeChild){
34694            m.activeChild.hide();
34695        }
34696        if(m.autoHideTimer){
34697            clearTimeout(m.autoHideTimer);
34698            delete m.autoHideTimer;
34699        }
34700    }
34701
34702    // private
34703    function onBeforeShow(m){
34704        var pm = m.parentMenu;
34705        if(!pm && !m.allowOtherMenus){
34706            hideAll();
34707        }else if(pm && pm.activeChild && active != m){
34708            pm.activeChild.hide();
34709        }
34710    }
34711
34712    // private
34713    function onMouseDown(e){
34714        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34715            hideAll();
34716        }
34717    }
34718
34719    // private
34720    function onBeforeCheck(mi, state){
34721        if(state){
34722            var g = groups[mi.group];
34723            for(var i = 0, l = g.length; i < l; i++){
34724                if(g[i] != mi){
34725                    g[i].setChecked(false);
34726                }
34727            }
34728        }
34729    }
34730
34731    return {
34732
34733        /**
34734         * Hides all menus that are currently visible
34735         */
34736        hideAll : function(){
34737             hideAll();  
34738        },
34739
34740        // private
34741        register : function(menu){
34742            if(!menus){
34743                init();
34744            }
34745            menus[menu.id] = menu;
34746            menu.on("beforehide", onBeforeHide);
34747            menu.on("hide", onHide);
34748            menu.on("beforeshow", onBeforeShow);
34749            menu.on("show", onShow);
34750            var g = menu.group;
34751            if(g && menu.events["checkchange"]){
34752                if(!groups[g]){
34753                    groups[g] = [];
34754                }
34755                groups[g].push(menu);
34756                menu.on("checkchange", onCheck);
34757            }
34758        },
34759
34760         /**
34761          * Returns a {@link Roo.menu.Menu} object
34762          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34763          * be used to generate and return a new Menu instance.
34764          */
34765        get : function(menu){
34766            if(typeof menu == "string"){ // menu id
34767                return menus[menu];
34768            }else if(menu.events){  // menu instance
34769                return menu;
34770            }else if(typeof menu.length == 'number'){ // array of menu items?
34771                return new Roo.menu.Menu({items:menu});
34772            }else{ // otherwise, must be a config
34773                return new Roo.menu.Menu(menu);
34774            }
34775        },
34776
34777        // private
34778        unregister : function(menu){
34779            delete menus[menu.id];
34780            menu.un("beforehide", onBeforeHide);
34781            menu.un("hide", onHide);
34782            menu.un("beforeshow", onBeforeShow);
34783            menu.un("show", onShow);
34784            var g = menu.group;
34785            if(g && menu.events["checkchange"]){
34786                groups[g].remove(menu);
34787                menu.un("checkchange", onCheck);
34788            }
34789        },
34790
34791        // private
34792        registerCheckable : function(menuItem){
34793            var g = menuItem.group;
34794            if(g){
34795                if(!groups[g]){
34796                    groups[g] = [];
34797                }
34798                groups[g].push(menuItem);
34799                menuItem.on("beforecheckchange", onBeforeCheck);
34800            }
34801        },
34802
34803        // private
34804        unregisterCheckable : function(menuItem){
34805            var g = menuItem.group;
34806            if(g){
34807                groups[g].remove(menuItem);
34808                menuItem.un("beforecheckchange", onBeforeCheck);
34809            }
34810        }
34811    };
34812 }();/*
34813  * Based on:
34814  * Ext JS Library 1.1.1
34815  * Copyright(c) 2006-2007, Ext JS, LLC.
34816  *
34817  * Originally Released Under LGPL - original licence link has changed is not relivant.
34818  *
34819  * Fork - LGPL
34820  * <script type="text/javascript">
34821  */
34822  
34823
34824 /**
34825  * @class Roo.menu.BaseItem
34826  * @extends Roo.Component
34827  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34828  * management and base configuration options shared by all menu components.
34829  * @constructor
34830  * Creates a new BaseItem
34831  * @param {Object} config Configuration options
34832  */
34833 Roo.menu.BaseItem = function(config){
34834     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34835
34836     this.addEvents({
34837         /**
34838          * @event click
34839          * Fires when this item is clicked
34840          * @param {Roo.menu.BaseItem} this
34841          * @param {Roo.EventObject} e
34842          */
34843         click: true,
34844         /**
34845          * @event activate
34846          * Fires when this item is activated
34847          * @param {Roo.menu.BaseItem} this
34848          */
34849         activate : true,
34850         /**
34851          * @event deactivate
34852          * Fires when this item is deactivated
34853          * @param {Roo.menu.BaseItem} this
34854          */
34855         deactivate : true
34856     });
34857
34858     if(this.handler){
34859         this.on("click", this.handler, this.scope, true);
34860     }
34861 };
34862
34863 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34864     /**
34865      * @cfg {Function} handler
34866      * A function that will handle the click event of this menu item (defaults to undefined)
34867      */
34868     /**
34869      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34870      */
34871     canActivate : false,
34872     /**
34873      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34874      */
34875     activeClass : "x-menu-item-active",
34876     /**
34877      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34878      */
34879     hideOnClick : true,
34880     /**
34881      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34882      */
34883     hideDelay : 100,
34884
34885     // private
34886     ctype: "Roo.menu.BaseItem",
34887
34888     // private
34889     actionMode : "container",
34890
34891     // private
34892     render : function(container, parentMenu){
34893         this.parentMenu = parentMenu;
34894         Roo.menu.BaseItem.superclass.render.call(this, container);
34895         this.container.menuItemId = this.id;
34896     },
34897
34898     // private
34899     onRender : function(container, position){
34900         this.el = Roo.get(this.el);
34901         container.dom.appendChild(this.el.dom);
34902     },
34903
34904     // private
34905     onClick : function(e){
34906         if(!this.disabled && this.fireEvent("click", this, e) !== false
34907                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34908             this.handleClick(e);
34909         }else{
34910             e.stopEvent();
34911         }
34912     },
34913
34914     // private
34915     activate : function(){
34916         if(this.disabled){
34917             return false;
34918         }
34919         var li = this.container;
34920         li.addClass(this.activeClass);
34921         this.region = li.getRegion().adjust(2, 2, -2, -2);
34922         this.fireEvent("activate", this);
34923         return true;
34924     },
34925
34926     // private
34927     deactivate : function(){
34928         this.container.removeClass(this.activeClass);
34929         this.fireEvent("deactivate", this);
34930     },
34931
34932     // private
34933     shouldDeactivate : function(e){
34934         return !this.region || !this.region.contains(e.getPoint());
34935     },
34936
34937     // private
34938     handleClick : function(e){
34939         if(this.hideOnClick){
34940             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34941         }
34942     },
34943
34944     // private
34945     expandMenu : function(autoActivate){
34946         // do nothing
34947     },
34948
34949     // private
34950     hideMenu : function(){
34951         // do nothing
34952     }
34953 });/*
34954  * Based on:
34955  * Ext JS Library 1.1.1
34956  * Copyright(c) 2006-2007, Ext JS, LLC.
34957  *
34958  * Originally Released Under LGPL - original licence link has changed is not relivant.
34959  *
34960  * Fork - LGPL
34961  * <script type="text/javascript">
34962  */
34963  
34964 /**
34965  * @class Roo.menu.Adapter
34966  * @extends Roo.menu.BaseItem
34967  * 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.
34968  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34969  * @constructor
34970  * Creates a new Adapter
34971  * @param {Object} config Configuration options
34972  */
34973 Roo.menu.Adapter = function(component, config){
34974     Roo.menu.Adapter.superclass.constructor.call(this, config);
34975     this.component = component;
34976 };
34977 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34978     // private
34979     canActivate : true,
34980
34981     // private
34982     onRender : function(container, position){
34983         this.component.render(container);
34984         this.el = this.component.getEl();
34985     },
34986
34987     // private
34988     activate : function(){
34989         if(this.disabled){
34990             return false;
34991         }
34992         this.component.focus();
34993         this.fireEvent("activate", this);
34994         return true;
34995     },
34996
34997     // private
34998     deactivate : function(){
34999         this.fireEvent("deactivate", this);
35000     },
35001
35002     // private
35003     disable : function(){
35004         this.component.disable();
35005         Roo.menu.Adapter.superclass.disable.call(this);
35006     },
35007
35008     // private
35009     enable : function(){
35010         this.component.enable();
35011         Roo.menu.Adapter.superclass.enable.call(this);
35012     }
35013 });/*
35014  * Based on:
35015  * Ext JS Library 1.1.1
35016  * Copyright(c) 2006-2007, Ext JS, LLC.
35017  *
35018  * Originally Released Under LGPL - original licence link has changed is not relivant.
35019  *
35020  * Fork - LGPL
35021  * <script type="text/javascript">
35022  */
35023
35024 /**
35025  * @class Roo.menu.TextItem
35026  * @extends Roo.menu.BaseItem
35027  * Adds a static text string to a menu, usually used as either a heading or group separator.
35028  * Note: old style constructor with text is still supported.
35029  * 
35030  * @constructor
35031  * Creates a new TextItem
35032  * @param {Object} cfg Configuration
35033  */
35034 Roo.menu.TextItem = function(cfg){
35035     if (typeof(cfg) == 'string') {
35036         this.text = cfg;
35037     } else {
35038         Roo.apply(this,cfg);
35039     }
35040     
35041     Roo.menu.TextItem.superclass.constructor.call(this);
35042 };
35043
35044 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35045     /**
35046      * @cfg {Boolean} text Text to show on item.
35047      */
35048     text : '',
35049     
35050     /**
35051      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35052      */
35053     hideOnClick : false,
35054     /**
35055      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35056      */
35057     itemCls : "x-menu-text",
35058
35059     // private
35060     onRender : function(){
35061         var s = document.createElement("span");
35062         s.className = this.itemCls;
35063         s.innerHTML = this.text;
35064         this.el = s;
35065         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35066     }
35067 });/*
35068  * Based on:
35069  * Ext JS Library 1.1.1
35070  * Copyright(c) 2006-2007, Ext JS, LLC.
35071  *
35072  * Originally Released Under LGPL - original licence link has changed is not relivant.
35073  *
35074  * Fork - LGPL
35075  * <script type="text/javascript">
35076  */
35077
35078 /**
35079  * @class Roo.menu.Separator
35080  * @extends Roo.menu.BaseItem
35081  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35082  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35083  * @constructor
35084  * @param {Object} config Configuration options
35085  */
35086 Roo.menu.Separator = function(config){
35087     Roo.menu.Separator.superclass.constructor.call(this, config);
35088 };
35089
35090 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35091     /**
35092      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35093      */
35094     itemCls : "x-menu-sep",
35095     /**
35096      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35097      */
35098     hideOnClick : false,
35099
35100     // private
35101     onRender : function(li){
35102         var s = document.createElement("span");
35103         s.className = this.itemCls;
35104         s.innerHTML = "&#160;";
35105         this.el = s;
35106         li.addClass("x-menu-sep-li");
35107         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35108     }
35109 });/*
35110  * Based on:
35111  * Ext JS Library 1.1.1
35112  * Copyright(c) 2006-2007, Ext JS, LLC.
35113  *
35114  * Originally Released Under LGPL - original licence link has changed is not relivant.
35115  *
35116  * Fork - LGPL
35117  * <script type="text/javascript">
35118  */
35119 /**
35120  * @class Roo.menu.Item
35121  * @extends Roo.menu.BaseItem
35122  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35123  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35124  * activation and click handling.
35125  * @constructor
35126  * Creates a new Item
35127  * @param {Object} config Configuration options
35128  */
35129 Roo.menu.Item = function(config){
35130     Roo.menu.Item.superclass.constructor.call(this, config);
35131     if(this.menu){
35132         this.menu = Roo.menu.MenuMgr.get(this.menu);
35133     }
35134 };
35135 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35136     
35137     /**
35138      * @cfg {String} text
35139      * The text to show on the menu item.
35140      */
35141     text: '',
35142      /**
35143      * @cfg {String} HTML to render in menu
35144      * The text to show on the menu item (HTML version).
35145      */
35146     html: '',
35147     /**
35148      * @cfg {String} icon
35149      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35150      */
35151     icon: undefined,
35152     /**
35153      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35154      */
35155     itemCls : "x-menu-item",
35156     /**
35157      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35158      */
35159     canActivate : true,
35160     /**
35161      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35162      */
35163     showDelay: 200,
35164     // doc'd in BaseItem
35165     hideDelay: 200,
35166
35167     // private
35168     ctype: "Roo.menu.Item",
35169     
35170     // private
35171     onRender : function(container, position){
35172         var el = document.createElement("a");
35173         el.hideFocus = true;
35174         el.unselectable = "on";
35175         el.href = this.href || "#";
35176         if(this.hrefTarget){
35177             el.target = this.hrefTarget;
35178         }
35179         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35180         
35181         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35182         
35183         el.innerHTML = String.format(
35184                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35185                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35186         this.el = el;
35187         Roo.menu.Item.superclass.onRender.call(this, container, position);
35188     },
35189
35190     /**
35191      * Sets the text to display in this menu item
35192      * @param {String} text The text to display
35193      * @param {Boolean} isHTML true to indicate text is pure html.
35194      */
35195     setText : function(text, isHTML){
35196         if (isHTML) {
35197             this.html = text;
35198         } else {
35199             this.text = text;
35200             this.html = '';
35201         }
35202         if(this.rendered){
35203             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35204      
35205             this.el.update(String.format(
35206                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35207                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35208             this.parentMenu.autoWidth();
35209         }
35210     },
35211
35212     // private
35213     handleClick : function(e){
35214         if(!this.href){ // if no link defined, stop the event automatically
35215             e.stopEvent();
35216         }
35217         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35218     },
35219
35220     // private
35221     activate : function(autoExpand){
35222         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35223             this.focus();
35224             if(autoExpand){
35225                 this.expandMenu();
35226             }
35227         }
35228         return true;
35229     },
35230
35231     // private
35232     shouldDeactivate : function(e){
35233         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35234             if(this.menu && this.menu.isVisible()){
35235                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35236             }
35237             return true;
35238         }
35239         return false;
35240     },
35241
35242     // private
35243     deactivate : function(){
35244         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35245         this.hideMenu();
35246     },
35247
35248     // private
35249     expandMenu : function(autoActivate){
35250         if(!this.disabled && this.menu){
35251             clearTimeout(this.hideTimer);
35252             delete this.hideTimer;
35253             if(!this.menu.isVisible() && !this.showTimer){
35254                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35255             }else if (this.menu.isVisible() && autoActivate){
35256                 this.menu.tryActivate(0, 1);
35257             }
35258         }
35259     },
35260
35261     // private
35262     deferExpand : function(autoActivate){
35263         delete this.showTimer;
35264         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35265         if(autoActivate){
35266             this.menu.tryActivate(0, 1);
35267         }
35268     },
35269
35270     // private
35271     hideMenu : function(){
35272         clearTimeout(this.showTimer);
35273         delete this.showTimer;
35274         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35275             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35276         }
35277     },
35278
35279     // private
35280     deferHide : function(){
35281         delete this.hideTimer;
35282         this.menu.hide();
35283     }
35284 });/*
35285  * Based on:
35286  * Ext JS Library 1.1.1
35287  * Copyright(c) 2006-2007, Ext JS, LLC.
35288  *
35289  * Originally Released Under LGPL - original licence link has changed is not relivant.
35290  *
35291  * Fork - LGPL
35292  * <script type="text/javascript">
35293  */
35294  
35295 /**
35296  * @class Roo.menu.CheckItem
35297  * @extends Roo.menu.Item
35298  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35299  * @constructor
35300  * Creates a new CheckItem
35301  * @param {Object} config Configuration options
35302  */
35303 Roo.menu.CheckItem = function(config){
35304     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35305     this.addEvents({
35306         /**
35307          * @event beforecheckchange
35308          * Fires before the checked value is set, providing an opportunity to cancel if needed
35309          * @param {Roo.menu.CheckItem} this
35310          * @param {Boolean} checked The new checked value that will be set
35311          */
35312         "beforecheckchange" : true,
35313         /**
35314          * @event checkchange
35315          * Fires after the checked value has been set
35316          * @param {Roo.menu.CheckItem} this
35317          * @param {Boolean} checked The checked value that was set
35318          */
35319         "checkchange" : true
35320     });
35321     if(this.checkHandler){
35322         this.on('checkchange', this.checkHandler, this.scope);
35323     }
35324 };
35325 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35326     /**
35327      * @cfg {String} group
35328      * All check items with the same group name will automatically be grouped into a single-select
35329      * radio button group (defaults to '')
35330      */
35331     /**
35332      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35333      */
35334     itemCls : "x-menu-item x-menu-check-item",
35335     /**
35336      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35337      */
35338     groupClass : "x-menu-group-item",
35339
35340     /**
35341      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35342      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35343      * initialized with checked = true will be rendered as checked.
35344      */
35345     checked: false,
35346
35347     // private
35348     ctype: "Roo.menu.CheckItem",
35349
35350     // private
35351     onRender : function(c){
35352         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35353         if(this.group){
35354             this.el.addClass(this.groupClass);
35355         }
35356         Roo.menu.MenuMgr.registerCheckable(this);
35357         if(this.checked){
35358             this.checked = false;
35359             this.setChecked(true, true);
35360         }
35361     },
35362
35363     // private
35364     destroy : function(){
35365         if(this.rendered){
35366             Roo.menu.MenuMgr.unregisterCheckable(this);
35367         }
35368         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35369     },
35370
35371     /**
35372      * Set the checked state of this item
35373      * @param {Boolean} checked The new checked value
35374      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35375      */
35376     setChecked : function(state, suppressEvent){
35377         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35378             if(this.container){
35379                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35380             }
35381             this.checked = state;
35382             if(suppressEvent !== true){
35383                 this.fireEvent("checkchange", this, state);
35384             }
35385         }
35386     },
35387
35388     // private
35389     handleClick : function(e){
35390        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35391            this.setChecked(!this.checked);
35392        }
35393        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35394     }
35395 });/*
35396  * Based on:
35397  * Ext JS Library 1.1.1
35398  * Copyright(c) 2006-2007, Ext JS, LLC.
35399  *
35400  * Originally Released Under LGPL - original licence link has changed is not relivant.
35401  *
35402  * Fork - LGPL
35403  * <script type="text/javascript">
35404  */
35405  
35406 /**
35407  * @class Roo.menu.DateItem
35408  * @extends Roo.menu.Adapter
35409  * A menu item that wraps the {@link Roo.DatPicker} component.
35410  * @constructor
35411  * Creates a new DateItem
35412  * @param {Object} config Configuration options
35413  */
35414 Roo.menu.DateItem = function(config){
35415     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35416     /** The Roo.DatePicker object @type Roo.DatePicker */
35417     this.picker = this.component;
35418     this.addEvents({select: true});
35419     
35420     this.picker.on("render", function(picker){
35421         picker.getEl().swallowEvent("click");
35422         picker.container.addClass("x-menu-date-item");
35423     });
35424
35425     this.picker.on("select", this.onSelect, this);
35426 };
35427
35428 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35429     // private
35430     onSelect : function(picker, date){
35431         this.fireEvent("select", this, date, picker);
35432         Roo.menu.DateItem.superclass.handleClick.call(this);
35433     }
35434 });/*
35435  * Based on:
35436  * Ext JS Library 1.1.1
35437  * Copyright(c) 2006-2007, Ext JS, LLC.
35438  *
35439  * Originally Released Under LGPL - original licence link has changed is not relivant.
35440  *
35441  * Fork - LGPL
35442  * <script type="text/javascript">
35443  */
35444  
35445 /**
35446  * @class Roo.menu.ColorItem
35447  * @extends Roo.menu.Adapter
35448  * A menu item that wraps the {@link Roo.ColorPalette} component.
35449  * @constructor
35450  * Creates a new ColorItem
35451  * @param {Object} config Configuration options
35452  */
35453 Roo.menu.ColorItem = function(config){
35454     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35455     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35456     this.palette = this.component;
35457     this.relayEvents(this.palette, ["select"]);
35458     if(this.selectHandler){
35459         this.on('select', this.selectHandler, this.scope);
35460     }
35461 };
35462 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35463  * Based on:
35464  * Ext JS Library 1.1.1
35465  * Copyright(c) 2006-2007, Ext JS, LLC.
35466  *
35467  * Originally Released Under LGPL - original licence link has changed is not relivant.
35468  *
35469  * Fork - LGPL
35470  * <script type="text/javascript">
35471  */
35472  
35473
35474 /**
35475  * @class Roo.menu.DateMenu
35476  * @extends Roo.menu.Menu
35477  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35478  * @constructor
35479  * Creates a new DateMenu
35480  * @param {Object} config Configuration options
35481  */
35482 Roo.menu.DateMenu = function(config){
35483     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35484     this.plain = true;
35485     var di = new Roo.menu.DateItem(config);
35486     this.add(di);
35487     /**
35488      * The {@link Roo.DatePicker} instance for this DateMenu
35489      * @type DatePicker
35490      */
35491     this.picker = di.picker;
35492     /**
35493      * @event select
35494      * @param {DatePicker} picker
35495      * @param {Date} date
35496      */
35497     this.relayEvents(di, ["select"]);
35498
35499     this.on('beforeshow', function(){
35500         if(this.picker){
35501             this.picker.hideMonthPicker(true);
35502         }
35503     }, this);
35504 };
35505 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35506     cls:'x-date-menu'
35507 });/*
35508  * Based on:
35509  * Ext JS Library 1.1.1
35510  * Copyright(c) 2006-2007, Ext JS, LLC.
35511  *
35512  * Originally Released Under LGPL - original licence link has changed is not relivant.
35513  *
35514  * Fork - LGPL
35515  * <script type="text/javascript">
35516  */
35517  
35518
35519 /**
35520  * @class Roo.menu.ColorMenu
35521  * @extends Roo.menu.Menu
35522  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35523  * @constructor
35524  * Creates a new ColorMenu
35525  * @param {Object} config Configuration options
35526  */
35527 Roo.menu.ColorMenu = function(config){
35528     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35529     this.plain = true;
35530     var ci = new Roo.menu.ColorItem(config);
35531     this.add(ci);
35532     /**
35533      * The {@link Roo.ColorPalette} instance for this ColorMenu
35534      * @type ColorPalette
35535      */
35536     this.palette = ci.palette;
35537     /**
35538      * @event select
35539      * @param {ColorPalette} palette
35540      * @param {String} color
35541      */
35542     this.relayEvents(ci, ["select"]);
35543 };
35544 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35545  * Based on:
35546  * Ext JS Library 1.1.1
35547  * Copyright(c) 2006-2007, Ext JS, LLC.
35548  *
35549  * Originally Released Under LGPL - original licence link has changed is not relivant.
35550  *
35551  * Fork - LGPL
35552  * <script type="text/javascript">
35553  */
35554  
35555 /**
35556  * @class Roo.form.Field
35557  * @extends Roo.BoxComponent
35558  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35559  * @constructor
35560  * Creates a new Field
35561  * @param {Object} config Configuration options
35562  */
35563 Roo.form.Field = function(config){
35564     Roo.form.Field.superclass.constructor.call(this, config);
35565 };
35566
35567 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35568     /**
35569      * @cfg {String} fieldLabel Label to use when rendering a form.
35570      */
35571        /**
35572      * @cfg {String} qtip Mouse over tip
35573      */
35574      
35575     /**
35576      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35577      */
35578     invalidClass : "x-form-invalid",
35579     /**
35580      * @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")
35581      */
35582     invalidText : "The value in this field is invalid",
35583     /**
35584      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35585      */
35586     focusClass : "x-form-focus",
35587     /**
35588      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35589       automatic validation (defaults to "keyup").
35590      */
35591     validationEvent : "keyup",
35592     /**
35593      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35594      */
35595     validateOnBlur : true,
35596     /**
35597      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35598      */
35599     validationDelay : 250,
35600     /**
35601      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35602      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35603      */
35604     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35605     /**
35606      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35607      */
35608     fieldClass : "x-form-field",
35609     /**
35610      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35611      *<pre>
35612 Value         Description
35613 -----------   ----------------------------------------------------------------------
35614 qtip          Display a quick tip when the user hovers over the field
35615 title         Display a default browser title attribute popup
35616 under         Add a block div beneath the field containing the error text
35617 side          Add an error icon to the right of the field with a popup on hover
35618 [element id]  Add the error text directly to the innerHTML of the specified element
35619 </pre>
35620      */
35621     msgTarget : 'qtip',
35622     /**
35623      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35624      */
35625     msgFx : 'normal',
35626
35627     /**
35628      * @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.
35629      */
35630     readOnly : false,
35631
35632     /**
35633      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35634      */
35635     disabled : false,
35636
35637     /**
35638      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35639      */
35640     inputType : undefined,
35641     
35642     /**
35643      * @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).
35644          */
35645         tabIndex : undefined,
35646         
35647     // private
35648     isFormField : true,
35649
35650     // private
35651     hasFocus : false,
35652     /**
35653      * @property {Roo.Element} fieldEl
35654      * Element Containing the rendered Field (with label etc.)
35655      */
35656     /**
35657      * @cfg {Mixed} value A value to initialize this field with.
35658      */
35659     value : undefined,
35660
35661     /**
35662      * @cfg {String} name The field's HTML name attribute.
35663      */
35664     /**
35665      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35666      */
35667
35668         // private ??
35669         initComponent : function(){
35670         Roo.form.Field.superclass.initComponent.call(this);
35671         this.addEvents({
35672             /**
35673              * @event focus
35674              * Fires when this field receives input focus.
35675              * @param {Roo.form.Field} this
35676              */
35677             focus : true,
35678             /**
35679              * @event blur
35680              * Fires when this field loses input focus.
35681              * @param {Roo.form.Field} this
35682              */
35683             blur : true,
35684             /**
35685              * @event specialkey
35686              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35687              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35688              * @param {Roo.form.Field} this
35689              * @param {Roo.EventObject} e The event object
35690              */
35691             specialkey : true,
35692             /**
35693              * @event change
35694              * Fires just before the field blurs if the field value has changed.
35695              * @param {Roo.form.Field} this
35696              * @param {Mixed} newValue The new value
35697              * @param {Mixed} oldValue The original value
35698              */
35699             change : true,
35700             /**
35701              * @event invalid
35702              * Fires after the field has been marked as invalid.
35703              * @param {Roo.form.Field} this
35704              * @param {String} msg The validation message
35705              */
35706             invalid : true,
35707             /**
35708              * @event valid
35709              * Fires after the field has been validated with no errors.
35710              * @param {Roo.form.Field} this
35711              */
35712             valid : true,
35713              /**
35714              * @event keyup
35715              * Fires after the key up
35716              * @param {Roo.form.Field} this
35717              * @param {Roo.EventObject}  e The event Object
35718              */
35719             keyup : true
35720         });
35721     },
35722
35723     /**
35724      * Returns the name attribute of the field if available
35725      * @return {String} name The field name
35726      */
35727     getName: function(){
35728          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35729     },
35730
35731     // private
35732     onRender : function(ct, position){
35733         Roo.form.Field.superclass.onRender.call(this, ct, position);
35734         if(!this.el){
35735             var cfg = this.getAutoCreate();
35736             if(!cfg.name){
35737                 cfg.name = this.name || this.id;
35738             }
35739             if(this.inputType){
35740                 cfg.type = this.inputType;
35741             }
35742             this.el = ct.createChild(cfg, position);
35743         }
35744         var type = this.el.dom.type;
35745         if(type){
35746             if(type == 'password'){
35747                 type = 'text';
35748             }
35749             this.el.addClass('x-form-'+type);
35750         }
35751         if(this.readOnly){
35752             this.el.dom.readOnly = true;
35753         }
35754         if(this.tabIndex !== undefined){
35755             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35756         }
35757
35758         this.el.addClass([this.fieldClass, this.cls]);
35759         this.initValue();
35760     },
35761
35762     /**
35763      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35764      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35765      * @return {Roo.form.Field} this
35766      */
35767     applyTo : function(target){
35768         this.allowDomMove = false;
35769         this.el = Roo.get(target);
35770         this.render(this.el.dom.parentNode);
35771         return this;
35772     },
35773
35774     // private
35775     initValue : function(){
35776         if(this.value !== undefined){
35777             this.setValue(this.value);
35778         }else if(this.el.dom.value.length > 0){
35779             this.setValue(this.el.dom.value);
35780         }
35781     },
35782
35783     /**
35784      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35785      */
35786     isDirty : function() {
35787         if(this.disabled) {
35788             return false;
35789         }
35790         return String(this.getValue()) !== String(this.originalValue);
35791     },
35792
35793     // private
35794     afterRender : function(){
35795         Roo.form.Field.superclass.afterRender.call(this);
35796         this.initEvents();
35797     },
35798
35799     // private
35800     fireKey : function(e){
35801         //Roo.log('field ' + e.getKey());
35802         if(e.isNavKeyPress()){
35803             this.fireEvent("specialkey", this, e);
35804         }
35805     },
35806
35807     /**
35808      * Resets the current field value to the originally loaded value and clears any validation messages
35809      */
35810     reset : function(){
35811         this.setValue(this.originalValue);
35812         this.clearInvalid();
35813     },
35814
35815     // private
35816     initEvents : function(){
35817         // safari killled keypress - so keydown is now used..
35818         this.el.on("keydown" , this.fireKey,  this);
35819         this.el.on("focus", this.onFocus,  this);
35820         this.el.on("blur", this.onBlur,  this);
35821         this.el.relayEvent('keyup', this);
35822
35823         // reference to original value for reset
35824         this.originalValue = this.getValue();
35825     },
35826
35827     // private
35828     onFocus : function(){
35829         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35830             this.el.addClass(this.focusClass);
35831         }
35832         if(!this.hasFocus){
35833             this.hasFocus = true;
35834             this.startValue = this.getValue();
35835             this.fireEvent("focus", this);
35836         }
35837     },
35838
35839     beforeBlur : Roo.emptyFn,
35840
35841     // private
35842     onBlur : function(){
35843         this.beforeBlur();
35844         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35845             this.el.removeClass(this.focusClass);
35846         }
35847         this.hasFocus = false;
35848         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35849             this.validate();
35850         }
35851         var v = this.getValue();
35852         if(String(v) !== String(this.startValue)){
35853             this.fireEvent('change', this, v, this.startValue);
35854         }
35855         this.fireEvent("blur", this);
35856     },
35857
35858     /**
35859      * Returns whether or not the field value is currently valid
35860      * @param {Boolean} preventMark True to disable marking the field invalid
35861      * @return {Boolean} True if the value is valid, else false
35862      */
35863     isValid : function(preventMark){
35864         if(this.disabled){
35865             return true;
35866         }
35867         var restore = this.preventMark;
35868         this.preventMark = preventMark === true;
35869         var v = this.validateValue(this.processValue(this.getRawValue()));
35870         this.preventMark = restore;
35871         return v;
35872     },
35873
35874     /**
35875      * Validates the field value
35876      * @return {Boolean} True if the value is valid, else false
35877      */
35878     validate : function(){
35879         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35880             this.clearInvalid();
35881             return true;
35882         }
35883         return false;
35884     },
35885
35886     processValue : function(value){
35887         return value;
35888     },
35889
35890     // private
35891     // Subclasses should provide the validation implementation by overriding this
35892     validateValue : function(value){
35893         return true;
35894     },
35895
35896     /**
35897      * Mark this field as invalid
35898      * @param {String} msg The validation message
35899      */
35900     markInvalid : function(msg){
35901         if(!this.rendered || this.preventMark){ // not rendered
35902             return;
35903         }
35904         this.el.addClass(this.invalidClass);
35905         msg = msg || this.invalidText;
35906         switch(this.msgTarget){
35907             case 'qtip':
35908                 this.el.dom.qtip = msg;
35909                 this.el.dom.qclass = 'x-form-invalid-tip';
35910                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35911                     Roo.QuickTips.enable();
35912                 }
35913                 break;
35914             case 'title':
35915                 this.el.dom.title = msg;
35916                 break;
35917             case 'under':
35918                 if(!this.errorEl){
35919                     var elp = this.el.findParent('.x-form-element', 5, true);
35920                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35921                     this.errorEl.setWidth(elp.getWidth(true)-20);
35922                 }
35923                 this.errorEl.update(msg);
35924                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35925                 break;
35926             case 'side':
35927                 if(!this.errorIcon){
35928                     var elp = this.el.findParent('.x-form-element', 5, true);
35929                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35930                 }
35931                 this.alignErrorIcon();
35932                 this.errorIcon.dom.qtip = msg;
35933                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35934                 this.errorIcon.show();
35935                 this.on('resize', this.alignErrorIcon, this);
35936                 break;
35937             default:
35938                 var t = Roo.getDom(this.msgTarget);
35939                 t.innerHTML = msg;
35940                 t.style.display = this.msgDisplay;
35941                 break;
35942         }
35943         this.fireEvent('invalid', this, msg);
35944     },
35945
35946     // private
35947     alignErrorIcon : function(){
35948         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35949     },
35950
35951     /**
35952      * Clear any invalid styles/messages for this field
35953      */
35954     clearInvalid : function(){
35955         if(!this.rendered || this.preventMark){ // not rendered
35956             return;
35957         }
35958         this.el.removeClass(this.invalidClass);
35959         switch(this.msgTarget){
35960             case 'qtip':
35961                 this.el.dom.qtip = '';
35962                 break;
35963             case 'title':
35964                 this.el.dom.title = '';
35965                 break;
35966             case 'under':
35967                 if(this.errorEl){
35968                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35969                 }
35970                 break;
35971             case 'side':
35972                 if(this.errorIcon){
35973                     this.errorIcon.dom.qtip = '';
35974                     this.errorIcon.hide();
35975                     this.un('resize', this.alignErrorIcon, this);
35976                 }
35977                 break;
35978             default:
35979                 var t = Roo.getDom(this.msgTarget);
35980                 t.innerHTML = '';
35981                 t.style.display = 'none';
35982                 break;
35983         }
35984         this.fireEvent('valid', this);
35985     },
35986
35987     /**
35988      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35989      * @return {Mixed} value The field value
35990      */
35991     getRawValue : function(){
35992         var v = this.el.getValue();
35993         if(v === this.emptyText){
35994             v = '';
35995         }
35996         return v;
35997     },
35998
35999     /**
36000      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36001      * @return {Mixed} value The field value
36002      */
36003     getValue : function(){
36004         var v = this.el.getValue();
36005         if(v === this.emptyText || v === undefined){
36006             v = '';
36007         }
36008         return v;
36009     },
36010
36011     /**
36012      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36013      * @param {Mixed} value The value to set
36014      */
36015     setRawValue : function(v){
36016         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36017     },
36018
36019     /**
36020      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36021      * @param {Mixed} value The value to set
36022      */
36023     setValue : function(v){
36024         this.value = v;
36025         if(this.rendered){
36026             this.el.dom.value = (v === null || v === undefined ? '' : v);
36027              this.validate();
36028         }
36029     },
36030
36031     adjustSize : function(w, h){
36032         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36033         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36034         return s;
36035     },
36036
36037     adjustWidth : function(tag, w){
36038         tag = tag.toLowerCase();
36039         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36040             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36041                 if(tag == 'input'){
36042                     return w + 2;
36043                 }
36044                 if(tag = 'textarea'){
36045                     return w-2;
36046                 }
36047             }else if(Roo.isOpera){
36048                 if(tag == 'input'){
36049                     return w + 2;
36050                 }
36051                 if(tag = 'textarea'){
36052                     return w-2;
36053                 }
36054             }
36055         }
36056         return w;
36057     }
36058 });
36059
36060
36061 // anything other than normal should be considered experimental
36062 Roo.form.Field.msgFx = {
36063     normal : {
36064         show: function(msgEl, f){
36065             msgEl.setDisplayed('block');
36066         },
36067
36068         hide : function(msgEl, f){
36069             msgEl.setDisplayed(false).update('');
36070         }
36071     },
36072
36073     slide : {
36074         show: function(msgEl, f){
36075             msgEl.slideIn('t', {stopFx:true});
36076         },
36077
36078         hide : function(msgEl, f){
36079             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36080         }
36081     },
36082
36083     slideRight : {
36084         show: function(msgEl, f){
36085             msgEl.fixDisplay();
36086             msgEl.alignTo(f.el, 'tl-tr');
36087             msgEl.slideIn('l', {stopFx:true});
36088         },
36089
36090         hide : function(msgEl, f){
36091             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36092         }
36093     }
36094 };/*
36095  * Based on:
36096  * Ext JS Library 1.1.1
36097  * Copyright(c) 2006-2007, Ext JS, LLC.
36098  *
36099  * Originally Released Under LGPL - original licence link has changed is not relivant.
36100  *
36101  * Fork - LGPL
36102  * <script type="text/javascript">
36103  */
36104  
36105
36106 /**
36107  * @class Roo.form.TextField
36108  * @extends Roo.form.Field
36109  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36110  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36111  * @constructor
36112  * Creates a new TextField
36113  * @param {Object} config Configuration options
36114  */
36115 Roo.form.TextField = function(config){
36116     Roo.form.TextField.superclass.constructor.call(this, config);
36117     this.addEvents({
36118         /**
36119          * @event autosize
36120          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36121          * according to the default logic, but this event provides a hook for the developer to apply additional
36122          * logic at runtime to resize the field if needed.
36123              * @param {Roo.form.Field} this This text field
36124              * @param {Number} width The new field width
36125              */
36126         autosize : true
36127     });
36128 };
36129
36130 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36131     /**
36132      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36133      */
36134     grow : false,
36135     /**
36136      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36137      */
36138     growMin : 30,
36139     /**
36140      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36141      */
36142     growMax : 800,
36143     /**
36144      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36145      */
36146     vtype : null,
36147     /**
36148      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36149      */
36150     maskRe : null,
36151     /**
36152      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36153      */
36154     disableKeyFilter : false,
36155     /**
36156      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36157      */
36158     allowBlank : true,
36159     /**
36160      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36161      */
36162     minLength : 0,
36163     /**
36164      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36165      */
36166     maxLength : Number.MAX_VALUE,
36167     /**
36168      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36169      */
36170     minLengthText : "The minimum length for this field is {0}",
36171     /**
36172      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36173      */
36174     maxLengthText : "The maximum length for this field is {0}",
36175     /**
36176      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36177      */
36178     selectOnFocus : false,
36179     /**
36180      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36181      */
36182     blankText : "This field is required",
36183     /**
36184      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36185      * If available, this function will be called only after the basic validators all return true, and will be passed the
36186      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36187      */
36188     validator : null,
36189     /**
36190      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36191      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36192      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36193      */
36194     regex : null,
36195     /**
36196      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36197      */
36198     regexText : "",
36199     /**
36200      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36201      */
36202     emptyText : null,
36203     /**
36204      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36205      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36206      */
36207     emptyClass : 'x-form-empty-field',
36208
36209     // private
36210     initEvents : function(){
36211         Roo.form.TextField.superclass.initEvents.call(this);
36212         if(this.validationEvent == 'keyup'){
36213             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36214             this.el.on('keyup', this.filterValidation, this);
36215         }
36216         else if(this.validationEvent !== false){
36217             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36218         }
36219         if(this.selectOnFocus || this.emptyText){
36220             this.on("focus", this.preFocus, this);
36221             if(this.emptyText){
36222                 this.on('blur', this.postBlur, this);
36223                 this.applyEmptyText();
36224             }
36225         }
36226         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36227             this.el.on("keypress", this.filterKeys, this);
36228         }
36229         if(this.grow){
36230             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36231             this.el.on("click", this.autoSize,  this);
36232         }
36233     },
36234
36235     processValue : function(value){
36236         if(this.stripCharsRe){
36237             var newValue = value.replace(this.stripCharsRe, '');
36238             if(newValue !== value){
36239                 this.setRawValue(newValue);
36240                 return newValue;
36241             }
36242         }
36243         return value;
36244     },
36245
36246     filterValidation : function(e){
36247         if(!e.isNavKeyPress()){
36248             this.validationTask.delay(this.validationDelay);
36249         }
36250     },
36251
36252     // private
36253     onKeyUp : function(e){
36254         if(!e.isNavKeyPress()){
36255             this.autoSize();
36256         }
36257     },
36258
36259     /**
36260      * Resets the current field value to the originally-loaded value and clears any validation messages.
36261      * Also adds emptyText and emptyClass if the original value was blank.
36262      */
36263     reset : function(){
36264         Roo.form.TextField.superclass.reset.call(this);
36265         this.applyEmptyText();
36266     },
36267
36268     applyEmptyText : function(){
36269         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36270             this.setRawValue(this.emptyText);
36271             this.el.addClass(this.emptyClass);
36272         }
36273     },
36274
36275     // private
36276     preFocus : function(){
36277         if(this.emptyText){
36278             if(this.el.dom.value == this.emptyText){
36279                 this.setRawValue('');
36280             }
36281             this.el.removeClass(this.emptyClass);
36282         }
36283         if(this.selectOnFocus){
36284             this.el.dom.select();
36285         }
36286     },
36287
36288     // private
36289     postBlur : function(){
36290         this.applyEmptyText();
36291     },
36292
36293     // private
36294     filterKeys : function(e){
36295         var k = e.getKey();
36296         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36297             return;
36298         }
36299         var c = e.getCharCode(), cc = String.fromCharCode(c);
36300         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36301             return;
36302         }
36303         if(!this.maskRe.test(cc)){
36304             e.stopEvent();
36305         }
36306     },
36307
36308     setValue : function(v){
36309         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36310             this.el.removeClass(this.emptyClass);
36311         }
36312         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36313         this.applyEmptyText();
36314         this.autoSize();
36315     },
36316
36317     /**
36318      * Validates a value according to the field's validation rules and marks the field as invalid
36319      * if the validation fails
36320      * @param {Mixed} value The value to validate
36321      * @return {Boolean} True if the value is valid, else false
36322      */
36323     validateValue : function(value){
36324         if(value.length < 1 || value === this.emptyText){ // if it's blank
36325              if(this.allowBlank){
36326                 this.clearInvalid();
36327                 return true;
36328              }else{
36329                 this.markInvalid(this.blankText);
36330                 return false;
36331              }
36332         }
36333         if(value.length < this.minLength){
36334             this.markInvalid(String.format(this.minLengthText, this.minLength));
36335             return false;
36336         }
36337         if(value.length > this.maxLength){
36338             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36339             return false;
36340         }
36341         if(this.vtype){
36342             var vt = Roo.form.VTypes;
36343             if(!vt[this.vtype](value, this)){
36344                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36345                 return false;
36346             }
36347         }
36348         if(typeof this.validator == "function"){
36349             var msg = this.validator(value);
36350             if(msg !== true){
36351                 this.markInvalid(msg);
36352                 return false;
36353             }
36354         }
36355         if(this.regex && !this.regex.test(value)){
36356             this.markInvalid(this.regexText);
36357             return false;
36358         }
36359         return true;
36360     },
36361
36362     /**
36363      * Selects text in this field
36364      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36365      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36366      */
36367     selectText : function(start, end){
36368         var v = this.getRawValue();
36369         if(v.length > 0){
36370             start = start === undefined ? 0 : start;
36371             end = end === undefined ? v.length : end;
36372             var d = this.el.dom;
36373             if(d.setSelectionRange){
36374                 d.setSelectionRange(start, end);
36375             }else if(d.createTextRange){
36376                 var range = d.createTextRange();
36377                 range.moveStart("character", start);
36378                 range.moveEnd("character", v.length-end);
36379                 range.select();
36380             }
36381         }
36382     },
36383
36384     /**
36385      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36386      * This only takes effect if grow = true, and fires the autosize event.
36387      */
36388     autoSize : function(){
36389         if(!this.grow || !this.rendered){
36390             return;
36391         }
36392         if(!this.metrics){
36393             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36394         }
36395         var el = this.el;
36396         var v = el.dom.value;
36397         var d = document.createElement('div');
36398         d.appendChild(document.createTextNode(v));
36399         v = d.innerHTML;
36400         d = null;
36401         v += "&#160;";
36402         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36403         this.el.setWidth(w);
36404         this.fireEvent("autosize", this, w);
36405     }
36406 });/*
36407  * Based on:
36408  * Ext JS Library 1.1.1
36409  * Copyright(c) 2006-2007, Ext JS, LLC.
36410  *
36411  * Originally Released Under LGPL - original licence link has changed is not relivant.
36412  *
36413  * Fork - LGPL
36414  * <script type="text/javascript">
36415  */
36416  
36417 /**
36418  * @class Roo.form.Hidden
36419  * @extends Roo.form.TextField
36420  * Simple Hidden element used on forms 
36421  * 
36422  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36423  * 
36424  * @constructor
36425  * Creates a new Hidden form element.
36426  * @param {Object} config Configuration options
36427  */
36428
36429
36430
36431 // easy hidden field...
36432 Roo.form.Hidden = function(config){
36433     Roo.form.Hidden.superclass.constructor.call(this, config);
36434 };
36435   
36436 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36437     fieldLabel:      '',
36438     inputType:      'hidden',
36439     width:          50,
36440     allowBlank:     true,
36441     labelSeparator: '',
36442     hidden:         true,
36443     itemCls :       'x-form-item-display-none'
36444
36445
36446 });
36447
36448
36449 /*
36450  * Based on:
36451  * Ext JS Library 1.1.1
36452  * Copyright(c) 2006-2007, Ext JS, LLC.
36453  *
36454  * Originally Released Under LGPL - original licence link has changed is not relivant.
36455  *
36456  * Fork - LGPL
36457  * <script type="text/javascript">
36458  */
36459  
36460 /**
36461  * @class Roo.form.TriggerField
36462  * @extends Roo.form.TextField
36463  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36464  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36465  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36466  * for which you can provide a custom implementation.  For example:
36467  * <pre><code>
36468 var trigger = new Roo.form.TriggerField();
36469 trigger.onTriggerClick = myTriggerFn;
36470 trigger.applyTo('my-field');
36471 </code></pre>
36472  *
36473  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36474  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36475  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36476  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36477  * @constructor
36478  * Create a new TriggerField.
36479  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36480  * to the base TextField)
36481  */
36482 Roo.form.TriggerField = function(config){
36483     this.mimicing = false;
36484     Roo.form.TriggerField.superclass.constructor.call(this, config);
36485 };
36486
36487 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36488     /**
36489      * @cfg {String} triggerClass A CSS class to apply to the trigger
36490      */
36491     /**
36492      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36493      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36494      */
36495     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36496     /**
36497      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36498      */
36499     hideTrigger:false,
36500
36501     /** @cfg {Boolean} grow @hide */
36502     /** @cfg {Number} growMin @hide */
36503     /** @cfg {Number} growMax @hide */
36504
36505     /**
36506      * @hide 
36507      * @method
36508      */
36509     autoSize: Roo.emptyFn,
36510     // private
36511     monitorTab : true,
36512     // private
36513     deferHeight : true,
36514
36515     
36516     actionMode : 'wrap',
36517     // private
36518     onResize : function(w, h){
36519         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36520         if(typeof w == 'number'){
36521             var x = w - this.trigger.getWidth();
36522             this.el.setWidth(this.adjustWidth('input', x));
36523             this.trigger.setStyle('left', x+'px');
36524         }
36525     },
36526
36527     // private
36528     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36529
36530     // private
36531     getResizeEl : function(){
36532         return this.wrap;
36533     },
36534
36535     // private
36536     getPositionEl : function(){
36537         return this.wrap;
36538     },
36539
36540     // private
36541     alignErrorIcon : function(){
36542         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36543     },
36544
36545     // private
36546     onRender : function(ct, position){
36547         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36548         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36549         this.trigger = this.wrap.createChild(this.triggerConfig ||
36550                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36551         if(this.hideTrigger){
36552             this.trigger.setDisplayed(false);
36553         }
36554         this.initTrigger();
36555         if(!this.width){
36556             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36557         }
36558     },
36559
36560     // private
36561     initTrigger : function(){
36562         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36563         this.trigger.addClassOnOver('x-form-trigger-over');
36564         this.trigger.addClassOnClick('x-form-trigger-click');
36565     },
36566
36567     // private
36568     onDestroy : function(){
36569         if(this.trigger){
36570             this.trigger.removeAllListeners();
36571             this.trigger.remove();
36572         }
36573         if(this.wrap){
36574             this.wrap.remove();
36575         }
36576         Roo.form.TriggerField.superclass.onDestroy.call(this);
36577     },
36578
36579     // private
36580     onFocus : function(){
36581         Roo.form.TriggerField.superclass.onFocus.call(this);
36582         if(!this.mimicing){
36583             this.wrap.addClass('x-trigger-wrap-focus');
36584             this.mimicing = true;
36585             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36586             if(this.monitorTab){
36587                 this.el.on("keydown", this.checkTab, this);
36588             }
36589         }
36590     },
36591
36592     // private
36593     checkTab : function(e){
36594         if(e.getKey() == e.TAB){
36595             this.triggerBlur();
36596         }
36597     },
36598
36599     // private
36600     onBlur : function(){
36601         // do nothing
36602     },
36603
36604     // private
36605     mimicBlur : function(e, t){
36606         if(!this.wrap.contains(t) && this.validateBlur()){
36607             this.triggerBlur();
36608         }
36609     },
36610
36611     // private
36612     triggerBlur : function(){
36613         this.mimicing = false;
36614         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36615         if(this.monitorTab){
36616             this.el.un("keydown", this.checkTab, this);
36617         }
36618         this.wrap.removeClass('x-trigger-wrap-focus');
36619         Roo.form.TriggerField.superclass.onBlur.call(this);
36620     },
36621
36622     // private
36623     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36624     validateBlur : function(e, t){
36625         return true;
36626     },
36627
36628     // private
36629     onDisable : function(){
36630         Roo.form.TriggerField.superclass.onDisable.call(this);
36631         if(this.wrap){
36632             this.wrap.addClass('x-item-disabled');
36633         }
36634     },
36635
36636     // private
36637     onEnable : function(){
36638         Roo.form.TriggerField.superclass.onEnable.call(this);
36639         if(this.wrap){
36640             this.wrap.removeClass('x-item-disabled');
36641         }
36642     },
36643
36644     // private
36645     onShow : function(){
36646         var ae = this.getActionEl();
36647         
36648         if(ae){
36649             ae.dom.style.display = '';
36650             ae.dom.style.visibility = 'visible';
36651         }
36652     },
36653
36654     // private
36655     
36656     onHide : function(){
36657         var ae = this.getActionEl();
36658         ae.dom.style.display = 'none';
36659     },
36660
36661     /**
36662      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36663      * by an implementing function.
36664      * @method
36665      * @param {EventObject} e
36666      */
36667     onTriggerClick : Roo.emptyFn
36668 });
36669
36670 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36671 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36672 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36673 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36674     initComponent : function(){
36675         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36676
36677         this.triggerConfig = {
36678             tag:'span', cls:'x-form-twin-triggers', cn:[
36679             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36680             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36681         ]};
36682     },
36683
36684     getTrigger : function(index){
36685         return this.triggers[index];
36686     },
36687
36688     initTrigger : function(){
36689         var ts = this.trigger.select('.x-form-trigger', true);
36690         this.wrap.setStyle('overflow', 'hidden');
36691         var triggerField = this;
36692         ts.each(function(t, all, index){
36693             t.hide = function(){
36694                 var w = triggerField.wrap.getWidth();
36695                 this.dom.style.display = 'none';
36696                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36697             };
36698             t.show = function(){
36699                 var w = triggerField.wrap.getWidth();
36700                 this.dom.style.display = '';
36701                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36702             };
36703             var triggerIndex = 'Trigger'+(index+1);
36704
36705             if(this['hide'+triggerIndex]){
36706                 t.dom.style.display = 'none';
36707             }
36708             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36709             t.addClassOnOver('x-form-trigger-over');
36710             t.addClassOnClick('x-form-trigger-click');
36711         }, this);
36712         this.triggers = ts.elements;
36713     },
36714
36715     onTrigger1Click : Roo.emptyFn,
36716     onTrigger2Click : Roo.emptyFn
36717 });/*
36718  * Based on:
36719  * Ext JS Library 1.1.1
36720  * Copyright(c) 2006-2007, Ext JS, LLC.
36721  *
36722  * Originally Released Under LGPL - original licence link has changed is not relivant.
36723  *
36724  * Fork - LGPL
36725  * <script type="text/javascript">
36726  */
36727  
36728 /**
36729  * @class Roo.form.TextArea
36730  * @extends Roo.form.TextField
36731  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36732  * support for auto-sizing.
36733  * @constructor
36734  * Creates a new TextArea
36735  * @param {Object} config Configuration options
36736  */
36737 Roo.form.TextArea = function(config){
36738     Roo.form.TextArea.superclass.constructor.call(this, config);
36739     // these are provided exchanges for backwards compat
36740     // minHeight/maxHeight were replaced by growMin/growMax to be
36741     // compatible with TextField growing config values
36742     if(this.minHeight !== undefined){
36743         this.growMin = this.minHeight;
36744     }
36745     if(this.maxHeight !== undefined){
36746         this.growMax = this.maxHeight;
36747     }
36748 };
36749
36750 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36751     /**
36752      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36753      */
36754     growMin : 60,
36755     /**
36756      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36757      */
36758     growMax: 1000,
36759     /**
36760      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36761      * in the field (equivalent to setting overflow: hidden, defaults to false)
36762      */
36763     preventScrollbars: false,
36764     /**
36765      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36766      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36767      */
36768
36769     // private
36770     onRender : function(ct, position){
36771         if(!this.el){
36772             this.defaultAutoCreate = {
36773                 tag: "textarea",
36774                 style:"width:300px;height:60px;",
36775                 autocomplete: "off"
36776             };
36777         }
36778         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36779         if(this.grow){
36780             this.textSizeEl = Roo.DomHelper.append(document.body, {
36781                 tag: "pre", cls: "x-form-grow-sizer"
36782             });
36783             if(this.preventScrollbars){
36784                 this.el.setStyle("overflow", "hidden");
36785             }
36786             this.el.setHeight(this.growMin);
36787         }
36788     },
36789
36790     onDestroy : function(){
36791         if(this.textSizeEl){
36792             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36793         }
36794         Roo.form.TextArea.superclass.onDestroy.call(this);
36795     },
36796
36797     // private
36798     onKeyUp : function(e){
36799         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36800             this.autoSize();
36801         }
36802     },
36803
36804     /**
36805      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36806      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36807      */
36808     autoSize : function(){
36809         if(!this.grow || !this.textSizeEl){
36810             return;
36811         }
36812         var el = this.el;
36813         var v = el.dom.value;
36814         var ts = this.textSizeEl;
36815
36816         ts.innerHTML = '';
36817         ts.appendChild(document.createTextNode(v));
36818         v = ts.innerHTML;
36819
36820         Roo.fly(ts).setWidth(this.el.getWidth());
36821         if(v.length < 1){
36822             v = "&#160;&#160;";
36823         }else{
36824             if(Roo.isIE){
36825                 v = v.replace(/\n/g, '<p>&#160;</p>');
36826             }
36827             v += "&#160;\n&#160;";
36828         }
36829         ts.innerHTML = v;
36830         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36831         if(h != this.lastHeight){
36832             this.lastHeight = h;
36833             this.el.setHeight(h);
36834             this.fireEvent("autosize", this, h);
36835         }
36836     }
36837 });/*
36838  * Based on:
36839  * Ext JS Library 1.1.1
36840  * Copyright(c) 2006-2007, Ext JS, LLC.
36841  *
36842  * Originally Released Under LGPL - original licence link has changed is not relivant.
36843  *
36844  * Fork - LGPL
36845  * <script type="text/javascript">
36846  */
36847  
36848
36849 /**
36850  * @class Roo.form.NumberField
36851  * @extends Roo.form.TextField
36852  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36853  * @constructor
36854  * Creates a new NumberField
36855  * @param {Object} config Configuration options
36856  */
36857 Roo.form.NumberField = function(config){
36858     Roo.form.NumberField.superclass.constructor.call(this, config);
36859 };
36860
36861 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36862     /**
36863      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36864      */
36865     fieldClass: "x-form-field x-form-num-field",
36866     /**
36867      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36868      */
36869     allowDecimals : true,
36870     /**
36871      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36872      */
36873     decimalSeparator : ".",
36874     /**
36875      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36876      */
36877     decimalPrecision : 2,
36878     /**
36879      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36880      */
36881     allowNegative : true,
36882     /**
36883      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36884      */
36885     minValue : Number.NEGATIVE_INFINITY,
36886     /**
36887      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36888      */
36889     maxValue : Number.MAX_VALUE,
36890     /**
36891      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36892      */
36893     minText : "The minimum value for this field is {0}",
36894     /**
36895      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36896      */
36897     maxText : "The maximum value for this field is {0}",
36898     /**
36899      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36900      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36901      */
36902     nanText : "{0} is not a valid number",
36903
36904     // private
36905     initEvents : function(){
36906         Roo.form.NumberField.superclass.initEvents.call(this);
36907         var allowed = "0123456789";
36908         if(this.allowDecimals){
36909             allowed += this.decimalSeparator;
36910         }
36911         if(this.allowNegative){
36912             allowed += "-";
36913         }
36914         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36915         var keyPress = function(e){
36916             var k = e.getKey();
36917             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36918                 return;
36919             }
36920             var c = e.getCharCode();
36921             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36922                 e.stopEvent();
36923             }
36924         };
36925         this.el.on("keypress", keyPress, this);
36926     },
36927
36928     // private
36929     validateValue : function(value){
36930         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36931             return false;
36932         }
36933         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36934              return true;
36935         }
36936         var num = this.parseValue(value);
36937         if(isNaN(num)){
36938             this.markInvalid(String.format(this.nanText, value));
36939             return false;
36940         }
36941         if(num < this.minValue){
36942             this.markInvalid(String.format(this.minText, this.minValue));
36943             return false;
36944         }
36945         if(num > this.maxValue){
36946             this.markInvalid(String.format(this.maxText, this.maxValue));
36947             return false;
36948         }
36949         return true;
36950     },
36951
36952     getValue : function(){
36953         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36954     },
36955
36956     // private
36957     parseValue : function(value){
36958         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36959         return isNaN(value) ? '' : value;
36960     },
36961
36962     // private
36963     fixPrecision : function(value){
36964         var nan = isNaN(value);
36965         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36966             return nan ? '' : value;
36967         }
36968         return parseFloat(value).toFixed(this.decimalPrecision);
36969     },
36970
36971     setValue : function(v){
36972         v = this.fixPrecision(v);
36973         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36974     },
36975
36976     // private
36977     decimalPrecisionFcn : function(v){
36978         return Math.floor(v);
36979     },
36980
36981     beforeBlur : function(){
36982         var v = this.parseValue(this.getRawValue());
36983         if(v){
36984             this.setValue(v);
36985         }
36986     }
36987 });/*
36988  * Based on:
36989  * Ext JS Library 1.1.1
36990  * Copyright(c) 2006-2007, Ext JS, LLC.
36991  *
36992  * Originally Released Under LGPL - original licence link has changed is not relivant.
36993  *
36994  * Fork - LGPL
36995  * <script type="text/javascript">
36996  */
36997  
36998 /**
36999  * @class Roo.form.DateField
37000  * @extends Roo.form.TriggerField
37001  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37002 * @constructor
37003 * Create a new DateField
37004 * @param {Object} config
37005  */
37006 Roo.form.DateField = function(config){
37007     Roo.form.DateField.superclass.constructor.call(this, config);
37008     
37009       this.addEvents({
37010          
37011         /**
37012          * @event select
37013          * Fires when a date is selected
37014              * @param {Roo.form.DateField} combo This combo box
37015              * @param {Date} date The date selected
37016              */
37017         'select' : true
37018          
37019     });
37020     
37021     
37022     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37023     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37024     this.ddMatch = null;
37025     if(this.disabledDates){
37026         var dd = this.disabledDates;
37027         var re = "(?:";
37028         for(var i = 0; i < dd.length; i++){
37029             re += dd[i];
37030             if(i != dd.length-1) re += "|";
37031         }
37032         this.ddMatch = new RegExp(re + ")");
37033     }
37034 };
37035
37036 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37037     /**
37038      * @cfg {String} format
37039      * The default date format string which can be overriden for localization support.  The format must be
37040      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37041      */
37042     format : "m/d/y",
37043     /**
37044      * @cfg {String} altFormats
37045      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37046      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37047      */
37048     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37049     /**
37050      * @cfg {Array} disabledDays
37051      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37052      */
37053     disabledDays : null,
37054     /**
37055      * @cfg {String} disabledDaysText
37056      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37057      */
37058     disabledDaysText : "Disabled",
37059     /**
37060      * @cfg {Array} disabledDates
37061      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37062      * expression so they are very powerful. Some examples:
37063      * <ul>
37064      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37065      * <li>["03/08", "09/16"] would disable those days for every year</li>
37066      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37067      * <li>["03/../2006"] would disable every day in March 2006</li>
37068      * <li>["^03"] would disable every day in every March</li>
37069      * </ul>
37070      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37071      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37072      */
37073     disabledDates : null,
37074     /**
37075      * @cfg {String} disabledDatesText
37076      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37077      */
37078     disabledDatesText : "Disabled",
37079     /**
37080      * @cfg {Date/String} minValue
37081      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37082      * valid format (defaults to null).
37083      */
37084     minValue : null,
37085     /**
37086      * @cfg {Date/String} maxValue
37087      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37088      * valid format (defaults to null).
37089      */
37090     maxValue : null,
37091     /**
37092      * @cfg {String} minText
37093      * The error text to display when the date in the cell is before minValue (defaults to
37094      * 'The date in this field must be after {minValue}').
37095      */
37096     minText : "The date in this field must be equal to or after {0}",
37097     /**
37098      * @cfg {String} maxText
37099      * The error text to display when the date in the cell is after maxValue (defaults to
37100      * 'The date in this field must be before {maxValue}').
37101      */
37102     maxText : "The date in this field must be equal to or before {0}",
37103     /**
37104      * @cfg {String} invalidText
37105      * The error text to display when the date in the field is invalid (defaults to
37106      * '{value} is not a valid date - it must be in the format {format}').
37107      */
37108     invalidText : "{0} is not a valid date - it must be in the format {1}",
37109     /**
37110      * @cfg {String} triggerClass
37111      * An additional CSS class used to style the trigger button.  The trigger will always get the
37112      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37113      * which displays a calendar icon).
37114      */
37115     triggerClass : 'x-form-date-trigger',
37116     
37117
37118     /**
37119      * @cfg {bool} useIso
37120      * if enabled, then the date field will use a hidden field to store the 
37121      * real value as iso formated date. default (false)
37122      */ 
37123     useIso : false,
37124     /**
37125      * @cfg {String/Object} autoCreate
37126      * A DomHelper element spec, or true for a default element spec (defaults to
37127      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37128      */ 
37129     // private
37130     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37131     
37132     // private
37133     hiddenField: false,
37134     
37135     onRender : function(ct, position)
37136     {
37137         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37138         if (this.useIso) {
37139             this.el.dom.removeAttribute('name'); 
37140             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37141                     'before', true);
37142             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37143             // prevent input submission
37144             this.hiddenName = this.name;
37145         }
37146             
37147             
37148     },
37149     
37150     // private
37151     validateValue : function(value)
37152     {
37153         value = this.formatDate(value);
37154         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37155             return false;
37156         }
37157         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37158              return true;
37159         }
37160         var svalue = value;
37161         value = this.parseDate(value);
37162         if(!value){
37163             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37164             return false;
37165         }
37166         var time = value.getTime();
37167         if(this.minValue && time < this.minValue.getTime()){
37168             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37169             return false;
37170         }
37171         if(this.maxValue && time > this.maxValue.getTime()){
37172             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37173             return false;
37174         }
37175         if(this.disabledDays){
37176             var day = value.getDay();
37177             for(var i = 0; i < this.disabledDays.length; i++) {
37178                 if(day === this.disabledDays[i]){
37179                     this.markInvalid(this.disabledDaysText);
37180                     return false;
37181                 }
37182             }
37183         }
37184         var fvalue = this.formatDate(value);
37185         if(this.ddMatch && this.ddMatch.test(fvalue)){
37186             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37187             return false;
37188         }
37189         return true;
37190     },
37191
37192     // private
37193     // Provides logic to override the default TriggerField.validateBlur which just returns true
37194     validateBlur : function(){
37195         return !this.menu || !this.menu.isVisible();
37196     },
37197
37198     /**
37199      * Returns the current date value of the date field.
37200      * @return {Date} The date value
37201      */
37202     getValue : function(){
37203         
37204         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37205     },
37206
37207     /**
37208      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37209      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37210      * (the default format used is "m/d/y").
37211      * <br />Usage:
37212      * <pre><code>
37213 //All of these calls set the same date value (May 4, 2006)
37214
37215 //Pass a date object:
37216 var dt = new Date('5/4/06');
37217 dateField.setValue(dt);
37218
37219 //Pass a date string (default format):
37220 dateField.setValue('5/4/06');
37221
37222 //Pass a date string (custom format):
37223 dateField.format = 'Y-m-d';
37224 dateField.setValue('2006-5-4');
37225 </code></pre>
37226      * @param {String/Date} date The date or valid date string
37227      */
37228     setValue : function(date){
37229         if (this.hiddenField) {
37230             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37231         }
37232         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37233     },
37234
37235     // private
37236     parseDate : function(value){
37237         if(!value || value instanceof Date){
37238             return value;
37239         }
37240         var v = Date.parseDate(value, this.format);
37241         if(!v && this.altFormats){
37242             if(!this.altFormatsArray){
37243                 this.altFormatsArray = this.altFormats.split("|");
37244             }
37245             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37246                 v = Date.parseDate(value, this.altFormatsArray[i]);
37247             }
37248         }
37249         return v;
37250     },
37251
37252     // private
37253     formatDate : function(date, fmt){
37254         return (!date || !(date instanceof Date)) ?
37255                date : date.dateFormat(fmt || this.format);
37256     },
37257
37258     // private
37259     menuListeners : {
37260         select: function(m, d){
37261             this.setValue(d);
37262             this.fireEvent('select', this, d);
37263         },
37264         show : function(){ // retain focus styling
37265             this.onFocus();
37266         },
37267         hide : function(){
37268             this.focus.defer(10, this);
37269             var ml = this.menuListeners;
37270             this.menu.un("select", ml.select,  this);
37271             this.menu.un("show", ml.show,  this);
37272             this.menu.un("hide", ml.hide,  this);
37273         }
37274     },
37275
37276     // private
37277     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37278     onTriggerClick : function(){
37279         if(this.disabled){
37280             return;
37281         }
37282         if(this.menu == null){
37283             this.menu = new Roo.menu.DateMenu();
37284         }
37285         Roo.apply(this.menu.picker,  {
37286             showClear: this.allowBlank,
37287             minDate : this.minValue,
37288             maxDate : this.maxValue,
37289             disabledDatesRE : this.ddMatch,
37290             disabledDatesText : this.disabledDatesText,
37291             disabledDays : this.disabledDays,
37292             disabledDaysText : this.disabledDaysText,
37293             format : this.format,
37294             minText : String.format(this.minText, this.formatDate(this.minValue)),
37295             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37296         });
37297         this.menu.on(Roo.apply({}, this.menuListeners, {
37298             scope:this
37299         }));
37300         this.menu.picker.setValue(this.getValue() || new Date());
37301         this.menu.show(this.el, "tl-bl?");
37302     },
37303
37304     beforeBlur : function(){
37305         var v = this.parseDate(this.getRawValue());
37306         if(v){
37307             this.setValue(v);
37308         }
37309     }
37310
37311     /** @cfg {Boolean} grow @hide */
37312     /** @cfg {Number} growMin @hide */
37313     /** @cfg {Number} growMax @hide */
37314     /**
37315      * @hide
37316      * @method autoSize
37317      */
37318 });/*
37319  * Based on:
37320  * Ext JS Library 1.1.1
37321  * Copyright(c) 2006-2007, Ext JS, LLC.
37322  *
37323  * Originally Released Under LGPL - original licence link has changed is not relivant.
37324  *
37325  * Fork - LGPL
37326  * <script type="text/javascript">
37327  */
37328  
37329
37330 /**
37331  * @class Roo.form.ComboBox
37332  * @extends Roo.form.TriggerField
37333  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37334  * @constructor
37335  * Create a new ComboBox.
37336  * @param {Object} config Configuration options
37337  */
37338 Roo.form.ComboBox = function(config){
37339     Roo.form.ComboBox.superclass.constructor.call(this, config);
37340     this.addEvents({
37341         /**
37342          * @event expand
37343          * Fires when the dropdown list is expanded
37344              * @param {Roo.form.ComboBox} combo This combo box
37345              */
37346         'expand' : true,
37347         /**
37348          * @event collapse
37349          * Fires when the dropdown list is collapsed
37350              * @param {Roo.form.ComboBox} combo This combo box
37351              */
37352         'collapse' : true,
37353         /**
37354          * @event beforeselect
37355          * Fires before a list item is selected. Return false to cancel the selection.
37356              * @param {Roo.form.ComboBox} combo This combo box
37357              * @param {Roo.data.Record} record The data record returned from the underlying store
37358              * @param {Number} index The index of the selected item in the dropdown list
37359              */
37360         'beforeselect' : true,
37361         /**
37362          * @event select
37363          * Fires when a list item is selected
37364              * @param {Roo.form.ComboBox} combo This combo box
37365              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37366              * @param {Number} index The index of the selected item in the dropdown list
37367              */
37368         'select' : true,
37369         /**
37370          * @event beforequery
37371          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37372          * The event object passed has these properties:
37373              * @param {Roo.form.ComboBox} combo This combo box
37374              * @param {String} query The query
37375              * @param {Boolean} forceAll true to force "all" query
37376              * @param {Boolean} cancel true to cancel the query
37377              * @param {Object} e The query event object
37378              */
37379         'beforequery': true,
37380          /**
37381          * @event add
37382          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37383              * @param {Roo.form.ComboBox} combo This combo box
37384              */
37385         'add' : true,
37386         /**
37387          * @event edit
37388          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37389              * @param {Roo.form.ComboBox} combo This combo box
37390              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37391              */
37392         'edit' : true
37393         
37394         
37395     });
37396     if(this.transform){
37397         this.allowDomMove = false;
37398         var s = Roo.getDom(this.transform);
37399         if(!this.hiddenName){
37400             this.hiddenName = s.name;
37401         }
37402         if(!this.store){
37403             this.mode = 'local';
37404             var d = [], opts = s.options;
37405             for(var i = 0, len = opts.length;i < len; i++){
37406                 var o = opts[i];
37407                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37408                 if(o.selected) {
37409                     this.value = value;
37410                 }
37411                 d.push([value, o.text]);
37412             }
37413             this.store = new Roo.data.SimpleStore({
37414                 'id': 0,
37415                 fields: ['value', 'text'],
37416                 data : d
37417             });
37418             this.valueField = 'value';
37419             this.displayField = 'text';
37420         }
37421         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37422         if(!this.lazyRender){
37423             this.target = true;
37424             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37425             s.parentNode.removeChild(s); // remove it
37426             this.render(this.el.parentNode);
37427         }else{
37428             s.parentNode.removeChild(s); // remove it
37429         }
37430
37431     }
37432     if (this.store) {
37433         this.store = Roo.factory(this.store, Roo.data);
37434     }
37435     
37436     this.selectedIndex = -1;
37437     if(this.mode == 'local'){
37438         if(config.queryDelay === undefined){
37439             this.queryDelay = 10;
37440         }
37441         if(config.minChars === undefined){
37442             this.minChars = 0;
37443         }
37444     }
37445 };
37446
37447 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37448     /**
37449      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37450      */
37451     /**
37452      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37453      * rendering into an Roo.Editor, defaults to false)
37454      */
37455     /**
37456      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37457      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37458      */
37459     /**
37460      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37461      */
37462     /**
37463      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37464      * the dropdown list (defaults to undefined, with no header element)
37465      */
37466
37467      /**
37468      * @cfg {String/Roo.Template} tpl The template to use to render the output
37469      */
37470      
37471     // private
37472     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37473     /**
37474      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37475      */
37476     listWidth: undefined,
37477     /**
37478      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37479      * mode = 'remote' or 'text' if mode = 'local')
37480      */
37481     displayField: undefined,
37482     /**
37483      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37484      * mode = 'remote' or 'value' if mode = 'local'). 
37485      * Note: use of a valueField requires the user make a selection
37486      * in order for a value to be mapped.
37487      */
37488     valueField: undefined,
37489     
37490     
37491     /**
37492      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37493      * field's data value (defaults to the underlying DOM element's name)
37494      */
37495     hiddenName: undefined,
37496     /**
37497      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37498      */
37499     listClass: '',
37500     /**
37501      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37502      */
37503     selectedClass: 'x-combo-selected',
37504     /**
37505      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37506      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37507      * which displays a downward arrow icon).
37508      */
37509     triggerClass : 'x-form-arrow-trigger',
37510     /**
37511      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37512      */
37513     shadow:'sides',
37514     /**
37515      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37516      * anchor positions (defaults to 'tl-bl')
37517      */
37518     listAlign: 'tl-bl?',
37519     /**
37520      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37521      */
37522     maxHeight: 300,
37523     /**
37524      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37525      * query specified by the allQuery config option (defaults to 'query')
37526      */
37527     triggerAction: 'query',
37528     /**
37529      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37530      * (defaults to 4, does not apply if editable = false)
37531      */
37532     minChars : 4,
37533     /**
37534      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37535      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37536      */
37537     typeAhead: false,
37538     /**
37539      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37540      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37541      */
37542     queryDelay: 500,
37543     /**
37544      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37545      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37546      */
37547     pageSize: 0,
37548     /**
37549      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37550      * when editable = true (defaults to false)
37551      */
37552     selectOnFocus:false,
37553     /**
37554      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37555      */
37556     queryParam: 'query',
37557     /**
37558      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37559      * when mode = 'remote' (defaults to 'Loading...')
37560      */
37561     loadingText: 'Loading...',
37562     /**
37563      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37564      */
37565     resizable: false,
37566     /**
37567      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37568      */
37569     handleHeight : 8,
37570     /**
37571      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37572      * traditional select (defaults to true)
37573      */
37574     editable: true,
37575     /**
37576      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37577      */
37578     allQuery: '',
37579     /**
37580      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37581      */
37582     mode: 'remote',
37583     /**
37584      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37585      * listWidth has a higher value)
37586      */
37587     minListWidth : 70,
37588     /**
37589      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37590      * allow the user to set arbitrary text into the field (defaults to false)
37591      */
37592     forceSelection:false,
37593     /**
37594      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37595      * if typeAhead = true (defaults to 250)
37596      */
37597     typeAheadDelay : 250,
37598     /**
37599      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37600      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37601      */
37602     valueNotFoundText : undefined,
37603     /**
37604      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37605      */
37606     blockFocus : false,
37607     
37608     /**
37609      * @cfg {Boolean} disableClear Disable showing of clear button.
37610      */
37611     disableClear : false,
37612     /**
37613      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37614      */
37615     alwaysQuery : false,
37616     
37617     //private
37618     addicon : false,
37619     editicon: false,
37620     
37621     // element that contains real text value.. (when hidden is used..)
37622      
37623     // private
37624     onRender : function(ct, position){
37625         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37626         if(this.hiddenName){
37627             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37628                     'before', true);
37629             this.hiddenField.value =
37630                 this.hiddenValue !== undefined ? this.hiddenValue :
37631                 this.value !== undefined ? this.value : '';
37632
37633             // prevent input submission
37634             this.el.dom.removeAttribute('name');
37635              
37636              
37637         }
37638         if(Roo.isGecko){
37639             this.el.dom.setAttribute('autocomplete', 'off');
37640         }
37641
37642         var cls = 'x-combo-list';
37643
37644         this.list = new Roo.Layer({
37645             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37646         });
37647
37648         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37649         this.list.setWidth(lw);
37650         this.list.swallowEvent('mousewheel');
37651         this.assetHeight = 0;
37652
37653         if(this.title){
37654             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37655             this.assetHeight += this.header.getHeight();
37656         }
37657
37658         this.innerList = this.list.createChild({cls:cls+'-inner'});
37659         this.innerList.on('mouseover', this.onViewOver, this);
37660         this.innerList.on('mousemove', this.onViewMove, this);
37661         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37662         
37663         if(this.allowBlank && !this.pageSize && !this.disableClear){
37664             this.footer = this.list.createChild({cls:cls+'-ft'});
37665             this.pageTb = new Roo.Toolbar(this.footer);
37666            
37667         }
37668         if(this.pageSize){
37669             this.footer = this.list.createChild({cls:cls+'-ft'});
37670             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37671                     {pageSize: this.pageSize});
37672             
37673         }
37674         
37675         if (this.pageTb && this.allowBlank && !this.disableClear) {
37676             var _this = this;
37677             this.pageTb.add(new Roo.Toolbar.Fill(), {
37678                 cls: 'x-btn-icon x-btn-clear',
37679                 text: '&#160;',
37680                 handler: function()
37681                 {
37682                     _this.collapse();
37683                     _this.clearValue();
37684                     _this.onSelect(false, -1);
37685                 }
37686             });
37687         }
37688         if (this.footer) {
37689             this.assetHeight += this.footer.getHeight();
37690         }
37691         
37692
37693         if(!this.tpl){
37694             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37695         }
37696
37697         this.view = new Roo.View(this.innerList, this.tpl, {
37698             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37699         });
37700
37701         this.view.on('click', this.onViewClick, this);
37702
37703         this.store.on('beforeload', this.onBeforeLoad, this);
37704         this.store.on('load', this.onLoad, this);
37705         this.store.on('loadexception', this.onLoadException, this);
37706
37707         if(this.resizable){
37708             this.resizer = new Roo.Resizable(this.list,  {
37709                pinned:true, handles:'se'
37710             });
37711             this.resizer.on('resize', function(r, w, h){
37712                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37713                 this.listWidth = w;
37714                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37715                 this.restrictHeight();
37716             }, this);
37717             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37718         }
37719         if(!this.editable){
37720             this.editable = true;
37721             this.setEditable(false);
37722         }  
37723         
37724         
37725         if (typeof(this.events.add.listeners) != 'undefined') {
37726             
37727             this.addicon = this.wrap.createChild(
37728                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37729        
37730             this.addicon.on('click', function(e) {
37731                 this.fireEvent('add', this);
37732             }, this);
37733         }
37734         if (typeof(this.events.edit.listeners) != 'undefined') {
37735             
37736             this.editicon = this.wrap.createChild(
37737                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37738             if (this.addicon) {
37739                 this.editicon.setStyle('margin-left', '40px');
37740             }
37741             this.editicon.on('click', function(e) {
37742                 
37743                 // we fire even  if inothing is selected..
37744                 this.fireEvent('edit', this, this.lastData );
37745                 
37746             }, this);
37747         }
37748         
37749         
37750         
37751     },
37752
37753     // private
37754     initEvents : function(){
37755         Roo.form.ComboBox.superclass.initEvents.call(this);
37756
37757         this.keyNav = new Roo.KeyNav(this.el, {
37758             "up" : function(e){
37759                 this.inKeyMode = true;
37760                 this.selectPrev();
37761             },
37762
37763             "down" : function(e){
37764                 if(!this.isExpanded()){
37765                     this.onTriggerClick();
37766                 }else{
37767                     this.inKeyMode = true;
37768                     this.selectNext();
37769                 }
37770             },
37771
37772             "enter" : function(e){
37773                 this.onViewClick();
37774                 //return true;
37775             },
37776
37777             "esc" : function(e){
37778                 this.collapse();
37779             },
37780
37781             "tab" : function(e){
37782                 this.onViewClick(false);
37783                 this.fireEvent("specialkey", this, e);
37784                 return true;
37785             },
37786
37787             scope : this,
37788
37789             doRelay : function(foo, bar, hname){
37790                 if(hname == 'down' || this.scope.isExpanded()){
37791                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37792                 }
37793                 return true;
37794             },
37795
37796             forceKeyDown: true
37797         });
37798         this.queryDelay = Math.max(this.queryDelay || 10,
37799                 this.mode == 'local' ? 10 : 250);
37800         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37801         if(this.typeAhead){
37802             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37803         }
37804         if(this.editable !== false){
37805             this.el.on("keyup", this.onKeyUp, this);
37806         }
37807         if(this.forceSelection){
37808             this.on('blur', this.doForce, this);
37809         }
37810     },
37811
37812     onDestroy : function(){
37813         if(this.view){
37814             this.view.setStore(null);
37815             this.view.el.removeAllListeners();
37816             this.view.el.remove();
37817             this.view.purgeListeners();
37818         }
37819         if(this.list){
37820             this.list.destroy();
37821         }
37822         if(this.store){
37823             this.store.un('beforeload', this.onBeforeLoad, this);
37824             this.store.un('load', this.onLoad, this);
37825             this.store.un('loadexception', this.onLoadException, this);
37826         }
37827         Roo.form.ComboBox.superclass.onDestroy.call(this);
37828     },
37829
37830     // private
37831     fireKey : function(e){
37832         if(e.isNavKeyPress() && !this.list.isVisible()){
37833             this.fireEvent("specialkey", this, e);
37834         }
37835     },
37836
37837     // private
37838     onResize: function(w, h){
37839         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37840         
37841         if(typeof w != 'number'){
37842             // we do not handle it!?!?
37843             return;
37844         }
37845         var tw = this.trigger.getWidth();
37846         tw += this.addicon ? this.addicon.getWidth() : 0;
37847         tw += this.editicon ? this.editicon.getWidth() : 0;
37848         var x = w - tw;
37849         this.el.setWidth( this.adjustWidth('input', x));
37850             
37851         this.trigger.setStyle('left', x+'px');
37852         
37853         if(this.list && this.listWidth === undefined){
37854             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37855             this.list.setWidth(lw);
37856             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37857         }
37858         
37859     
37860         
37861     },
37862
37863     /**
37864      * Allow or prevent the user from directly editing the field text.  If false is passed,
37865      * the user will only be able to select from the items defined in the dropdown list.  This method
37866      * is the runtime equivalent of setting the 'editable' config option at config time.
37867      * @param {Boolean} value True to allow the user to directly edit the field text
37868      */
37869     setEditable : function(value){
37870         if(value == this.editable){
37871             return;
37872         }
37873         this.editable = value;
37874         if(!value){
37875             this.el.dom.setAttribute('readOnly', true);
37876             this.el.on('mousedown', this.onTriggerClick,  this);
37877             this.el.addClass('x-combo-noedit');
37878         }else{
37879             this.el.dom.setAttribute('readOnly', false);
37880             this.el.un('mousedown', this.onTriggerClick,  this);
37881             this.el.removeClass('x-combo-noedit');
37882         }
37883     },
37884
37885     // private
37886     onBeforeLoad : function(){
37887         if(!this.hasFocus){
37888             return;
37889         }
37890         this.innerList.update(this.loadingText ?
37891                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37892         this.restrictHeight();
37893         this.selectedIndex = -1;
37894     },
37895
37896     // private
37897     onLoad : function(){
37898         if(!this.hasFocus){
37899             return;
37900         }
37901         if(this.store.getCount() > 0){
37902             this.expand();
37903             this.restrictHeight();
37904             if(this.lastQuery == this.allQuery){
37905                 if(this.editable){
37906                     this.el.dom.select();
37907                 }
37908                 if(!this.selectByValue(this.value, true)){
37909                     this.select(0, true);
37910                 }
37911             }else{
37912                 this.selectNext();
37913                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37914                     this.taTask.delay(this.typeAheadDelay);
37915                 }
37916             }
37917         }else{
37918             this.onEmptyResults();
37919         }
37920         //this.el.focus();
37921     },
37922     // private
37923     onLoadException : function()
37924     {
37925         this.collapse();
37926         Roo.log(this.store.reader.jsonData);
37927         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37928             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37929         }
37930         
37931         
37932     },
37933     // private
37934     onTypeAhead : function(){
37935         if(this.store.getCount() > 0){
37936             var r = this.store.getAt(0);
37937             var newValue = r.data[this.displayField];
37938             var len = newValue.length;
37939             var selStart = this.getRawValue().length;
37940             if(selStart != len){
37941                 this.setRawValue(newValue);
37942                 this.selectText(selStart, newValue.length);
37943             }
37944         }
37945     },
37946
37947     // private
37948     onSelect : function(record, index){
37949         if(this.fireEvent('beforeselect', this, record, index) !== false){
37950             this.setFromData(index > -1 ? record.data : false);
37951             this.collapse();
37952             this.fireEvent('select', this, record, index);
37953         }
37954     },
37955
37956     /**
37957      * Returns the currently selected field value or empty string if no value is set.
37958      * @return {String} value The selected value
37959      */
37960     getValue : function(){
37961         if(this.valueField){
37962             return typeof this.value != 'undefined' ? this.value : '';
37963         }else{
37964             return Roo.form.ComboBox.superclass.getValue.call(this);
37965         }
37966     },
37967
37968     /**
37969      * Clears any text/value currently set in the field
37970      */
37971     clearValue : function(){
37972         if(this.hiddenField){
37973             this.hiddenField.value = '';
37974         }
37975         this.value = '';
37976         this.setRawValue('');
37977         this.lastSelectionText = '';
37978         this.applyEmptyText();
37979     },
37980
37981     /**
37982      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37983      * will be displayed in the field.  If the value does not match the data value of an existing item,
37984      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37985      * Otherwise the field will be blank (although the value will still be set).
37986      * @param {String} value The value to match
37987      */
37988     setValue : function(v){
37989         var text = v;
37990         if(this.valueField){
37991             var r = this.findRecord(this.valueField, v);
37992             if(r){
37993                 text = r.data[this.displayField];
37994             }else if(this.valueNotFoundText !== undefined){
37995                 text = this.valueNotFoundText;
37996             }
37997         }
37998         this.lastSelectionText = text;
37999         if(this.hiddenField){
38000             this.hiddenField.value = v;
38001         }
38002         Roo.form.ComboBox.superclass.setValue.call(this, text);
38003         this.value = v;
38004     },
38005     /**
38006      * @property {Object} the last set data for the element
38007      */
38008     
38009     lastData : false,
38010     /**
38011      * Sets the value of the field based on a object which is related to the record format for the store.
38012      * @param {Object} value the value to set as. or false on reset?
38013      */
38014     setFromData : function(o){
38015         var dv = ''; // display value
38016         var vv = ''; // value value..
38017         this.lastData = o;
38018         if (this.displayField) {
38019             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
38020         } else {
38021             // this is an error condition!!!
38022             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
38023         }
38024         
38025         if(this.valueField){
38026             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
38027         }
38028         if(this.hiddenField){
38029             this.hiddenField.value = vv;
38030             
38031             this.lastSelectionText = dv;
38032             Roo.form.ComboBox.superclass.setValue.call(this, dv);
38033             this.value = vv;
38034             return;
38035         }
38036         // no hidden field.. - we store the value in 'value', but still display
38037         // display field!!!!
38038         this.lastSelectionText = dv;
38039         Roo.form.ComboBox.superclass.setValue.call(this, dv);
38040         this.value = vv;
38041         
38042         
38043     },
38044     // private
38045     reset : function(){
38046         // overridden so that last data is reset..
38047         this.setValue(this.originalValue);
38048         this.clearInvalid();
38049         this.lastData = false;
38050     },
38051     // private
38052     findRecord : function(prop, value){
38053         var record;
38054         if(this.store.getCount() > 0){
38055             this.store.each(function(r){
38056                 if(r.data[prop] == value){
38057                     record = r;
38058                     return false;
38059                 }
38060                 return true;
38061             });
38062         }
38063         return record;
38064     },
38065     
38066     getName: function()
38067     {
38068         // returns hidden if it's set..
38069         if (!this.rendered) {return ''};
38070         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38071         
38072     },
38073     // private
38074     onViewMove : function(e, t){
38075         this.inKeyMode = false;
38076     },
38077
38078     // private
38079     onViewOver : function(e, t){
38080         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38081             return;
38082         }
38083         var item = this.view.findItemFromChild(t);
38084         if(item){
38085             var index = this.view.indexOf(item);
38086             this.select(index, false);
38087         }
38088     },
38089
38090     // private
38091     onViewClick : function(doFocus)
38092     {
38093         var index = this.view.getSelectedIndexes()[0];
38094         var r = this.store.getAt(index);
38095         if(r){
38096             this.onSelect(r, index);
38097         }
38098         if(doFocus !== false && !this.blockFocus){
38099             this.el.focus();
38100         }
38101     },
38102
38103     // private
38104     restrictHeight : function(){
38105         this.innerList.dom.style.height = '';
38106         var inner = this.innerList.dom;
38107         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38108         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38109         this.list.beginUpdate();
38110         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38111         this.list.alignTo(this.el, this.listAlign);
38112         this.list.endUpdate();
38113     },
38114
38115     // private
38116     onEmptyResults : function(){
38117         this.collapse();
38118     },
38119
38120     /**
38121      * Returns true if the dropdown list is expanded, else false.
38122      */
38123     isExpanded : function(){
38124         return this.list.isVisible();
38125     },
38126
38127     /**
38128      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38129      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38130      * @param {String} value The data value of the item to select
38131      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38132      * selected item if it is not currently in view (defaults to true)
38133      * @return {Boolean} True if the value matched an item in the list, else false
38134      */
38135     selectByValue : function(v, scrollIntoView){
38136         if(v !== undefined && v !== null){
38137             var r = this.findRecord(this.valueField || this.displayField, v);
38138             if(r){
38139                 this.select(this.store.indexOf(r), scrollIntoView);
38140                 return true;
38141             }
38142         }
38143         return false;
38144     },
38145
38146     /**
38147      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38148      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38149      * @param {Number} index The zero-based index of the list item to select
38150      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38151      * selected item if it is not currently in view (defaults to true)
38152      */
38153     select : function(index, scrollIntoView){
38154         this.selectedIndex = index;
38155         this.view.select(index);
38156         if(scrollIntoView !== false){
38157             var el = this.view.getNode(index);
38158             if(el){
38159                 this.innerList.scrollChildIntoView(el, false);
38160             }
38161         }
38162     },
38163
38164     // private
38165     selectNext : function(){
38166         var ct = this.store.getCount();
38167         if(ct > 0){
38168             if(this.selectedIndex == -1){
38169                 this.select(0);
38170             }else if(this.selectedIndex < ct-1){
38171                 this.select(this.selectedIndex+1);
38172             }
38173         }
38174     },
38175
38176     // private
38177     selectPrev : function(){
38178         var ct = this.store.getCount();
38179         if(ct > 0){
38180             if(this.selectedIndex == -1){
38181                 this.select(0);
38182             }else if(this.selectedIndex != 0){
38183                 this.select(this.selectedIndex-1);
38184             }
38185         }
38186     },
38187
38188     // private
38189     onKeyUp : function(e){
38190         if(this.editable !== false && !e.isSpecialKey()){
38191             this.lastKey = e.getKey();
38192             this.dqTask.delay(this.queryDelay);
38193         }
38194     },
38195
38196     // private
38197     validateBlur : function(){
38198         return !this.list || !this.list.isVisible();   
38199     },
38200
38201     // private
38202     initQuery : function(){
38203         this.doQuery(this.getRawValue());
38204     },
38205
38206     // private
38207     doForce : function(){
38208         if(this.el.dom.value.length > 0){
38209             this.el.dom.value =
38210                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38211             this.applyEmptyText();
38212         }
38213     },
38214
38215     /**
38216      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38217      * query allowing the query action to be canceled if needed.
38218      * @param {String} query The SQL query to execute
38219      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38220      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38221      * saved in the current store (defaults to false)
38222      */
38223     doQuery : function(q, forceAll){
38224         if(q === undefined || q === null){
38225             q = '';
38226         }
38227         var qe = {
38228             query: q,
38229             forceAll: forceAll,
38230             combo: this,
38231             cancel:false
38232         };
38233         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38234             return false;
38235         }
38236         q = qe.query;
38237         forceAll = qe.forceAll;
38238         if(forceAll === true || (q.length >= this.minChars)){
38239             if(this.lastQuery != q || this.alwaysQuery){
38240                 this.lastQuery = q;
38241                 if(this.mode == 'local'){
38242                     this.selectedIndex = -1;
38243                     if(forceAll){
38244                         this.store.clearFilter();
38245                     }else{
38246                         this.store.filter(this.displayField, q);
38247                     }
38248                     this.onLoad();
38249                 }else{
38250                     this.store.baseParams[this.queryParam] = q;
38251                     this.store.load({
38252                         params: this.getParams(q)
38253                     });
38254                     this.expand();
38255                 }
38256             }else{
38257                 this.selectedIndex = -1;
38258                 this.onLoad();   
38259             }
38260         }
38261     },
38262
38263     // private
38264     getParams : function(q){
38265         var p = {};
38266         //p[this.queryParam] = q;
38267         if(this.pageSize){
38268             p.start = 0;
38269             p.limit = this.pageSize;
38270         }
38271         return p;
38272     },
38273
38274     /**
38275      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38276      */
38277     collapse : function(){
38278         if(!this.isExpanded()){
38279             return;
38280         }
38281         this.list.hide();
38282         Roo.get(document).un('mousedown', this.collapseIf, this);
38283         Roo.get(document).un('mousewheel', this.collapseIf, this);
38284         if (!this.editable) {
38285             Roo.get(document).un('keydown', this.listKeyPress, this);
38286         }
38287         this.fireEvent('collapse', this);
38288     },
38289
38290     // private
38291     collapseIf : function(e){
38292         if(!e.within(this.wrap) && !e.within(this.list)){
38293             this.collapse();
38294         }
38295     },
38296
38297     /**
38298      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38299      */
38300     expand : function(){
38301         if(this.isExpanded() || !this.hasFocus){
38302             return;
38303         }
38304         this.list.alignTo(this.el, this.listAlign);
38305         this.list.show();
38306         Roo.get(document).on('mousedown', this.collapseIf, this);
38307         Roo.get(document).on('mousewheel', this.collapseIf, this);
38308         if (!this.editable) {
38309             Roo.get(document).on('keydown', this.listKeyPress, this);
38310         }
38311         
38312         this.fireEvent('expand', this);
38313     },
38314
38315     // private
38316     // Implements the default empty TriggerField.onTriggerClick function
38317     onTriggerClick : function(){
38318         if(this.disabled){
38319             return;
38320         }
38321         if(this.isExpanded()){
38322             this.collapse();
38323             if (!this.blockFocus) {
38324                 this.el.focus();
38325             }
38326             
38327         }else {
38328             this.hasFocus = true;
38329             if(this.triggerAction == 'all') {
38330                 this.doQuery(this.allQuery, true);
38331             } else {
38332                 this.doQuery(this.getRawValue());
38333             }
38334             if (!this.blockFocus) {
38335                 this.el.focus();
38336             }
38337         }
38338     },
38339     listKeyPress : function(e)
38340     {
38341         //Roo.log('listkeypress');
38342         // scroll to first matching element based on key pres..
38343         if (e.isSpecialKey()) {
38344             return false;
38345         }
38346         var k = String.fromCharCode(e.getKey()).toUpperCase();
38347         //Roo.log(k);
38348         var match  = false;
38349         var csel = this.view.getSelectedNodes();
38350         var cselitem = false;
38351         if (csel.length) {
38352             var ix = this.view.indexOf(csel[0]);
38353             cselitem  = this.store.getAt(ix);
38354             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38355                 cselitem = false;
38356             }
38357             
38358         }
38359         
38360         this.store.each(function(v) { 
38361             if (cselitem) {
38362                 // start at existing selection.
38363                 if (cselitem.id == v.id) {
38364                     cselitem = false;
38365                 }
38366                 return;
38367             }
38368                 
38369             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38370                 match = this.store.indexOf(v);
38371                 return false;
38372             }
38373         }, this);
38374         
38375         if (match === false) {
38376             return true; // no more action?
38377         }
38378         // scroll to?
38379         this.view.select(match);
38380         var sn = Roo.get(this.view.getSelectedNodes()[0])
38381         sn.scrollIntoView(sn.dom.parentNode, false);
38382     }
38383
38384     /** 
38385     * @cfg {Boolean} grow 
38386     * @hide 
38387     */
38388     /** 
38389     * @cfg {Number} growMin 
38390     * @hide 
38391     */
38392     /** 
38393     * @cfg {Number} growMax 
38394     * @hide 
38395     */
38396     /**
38397      * @hide
38398      * @method autoSize
38399      */
38400 });/*
38401  * Based on:
38402  * Ext JS Library 1.1.1
38403  * Copyright(c) 2006-2007, Ext JS, LLC.
38404  *
38405  * Originally Released Under LGPL - original licence link has changed is not relivant.
38406  *
38407  * Fork - LGPL
38408  * <script type="text/javascript">
38409  */
38410 /**
38411  * @class Roo.form.Checkbox
38412  * @extends Roo.form.Field
38413  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38414  * @constructor
38415  * Creates a new Checkbox
38416  * @param {Object} config Configuration options
38417  */
38418 Roo.form.Checkbox = function(config){
38419     Roo.form.Checkbox.superclass.constructor.call(this, config);
38420     this.addEvents({
38421         /**
38422          * @event check
38423          * Fires when the checkbox is checked or unchecked.
38424              * @param {Roo.form.Checkbox} this This checkbox
38425              * @param {Boolean} checked The new checked value
38426              */
38427         check : true
38428     });
38429 };
38430
38431 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38432     /**
38433      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38434      */
38435     focusClass : undefined,
38436     /**
38437      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38438      */
38439     fieldClass: "x-form-field",
38440     /**
38441      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38442      */
38443     checked: false,
38444     /**
38445      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38446      * {tag: "input", type: "checkbox", autocomplete: "off"})
38447      */
38448     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38449     /**
38450      * @cfg {String} boxLabel The text that appears beside the checkbox
38451      */
38452     boxLabel : "",
38453     /**
38454      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38455      */  
38456     inputValue : '1',
38457     /**
38458      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38459      */
38460      valueOff: '0', // value when not checked..
38461
38462     actionMode : 'viewEl', 
38463     //
38464     // private
38465     itemCls : 'x-menu-check-item x-form-item',
38466     groupClass : 'x-menu-group-item',
38467     inputType : 'hidden',
38468     
38469     
38470     inSetChecked: false, // check that we are not calling self...
38471     
38472     inputElement: false, // real input element?
38473     basedOn: false, // ????
38474     
38475     isFormField: true, // not sure where this is needed!!!!
38476
38477     onResize : function(){
38478         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38479         if(!this.boxLabel){
38480             this.el.alignTo(this.wrap, 'c-c');
38481         }
38482     },
38483
38484     initEvents : function(){
38485         Roo.form.Checkbox.superclass.initEvents.call(this);
38486         this.el.on("click", this.onClick,  this);
38487         this.el.on("change", this.onClick,  this);
38488     },
38489
38490
38491     getResizeEl : function(){
38492         return this.wrap;
38493     },
38494
38495     getPositionEl : function(){
38496         return this.wrap;
38497     },
38498
38499     // private
38500     onRender : function(ct, position){
38501         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38502         /*
38503         if(this.inputValue !== undefined){
38504             this.el.dom.value = this.inputValue;
38505         }
38506         */
38507         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38508         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38509         var viewEl = this.wrap.createChild({ 
38510             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38511         this.viewEl = viewEl;   
38512         this.wrap.on('click', this.onClick,  this); 
38513         
38514         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38515         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38516         
38517         
38518         
38519         if(this.boxLabel){
38520             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38521         //    viewEl.on('click', this.onClick,  this); 
38522         }
38523         //if(this.checked){
38524             this.setChecked(this.checked);
38525         //}else{
38526             //this.checked = this.el.dom;
38527         //}
38528
38529     },
38530
38531     // private
38532     initValue : Roo.emptyFn,
38533
38534     /**
38535      * Returns the checked state of the checkbox.
38536      * @return {Boolean} True if checked, else false
38537      */
38538     getValue : function(){
38539         if(this.el){
38540             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38541         }
38542         return this.valueOff;
38543         
38544     },
38545
38546         // private
38547     onClick : function(){ 
38548         this.setChecked(!this.checked);
38549
38550         //if(this.el.dom.checked != this.checked){
38551         //    this.setValue(this.el.dom.checked);
38552        // }
38553     },
38554
38555     /**
38556      * Sets the checked state of the checkbox.
38557      * On is always based on a string comparison between inputValue and the param.
38558      * @param {Boolean/String} value - the value to set 
38559      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38560      */
38561     setValue : function(v,suppressEvent){
38562         
38563         
38564         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38565         //if(this.el && this.el.dom){
38566         //    this.el.dom.checked = this.checked;
38567         //    this.el.dom.defaultChecked = this.checked;
38568         //}
38569         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38570         //this.fireEvent("check", this, this.checked);
38571     },
38572     // private..
38573     setChecked : function(state,suppressEvent)
38574     {
38575         if (this.inSetChecked) {
38576             this.checked = state;
38577             return;
38578         }
38579         
38580     
38581         if(this.wrap){
38582             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38583         }
38584         this.checked = state;
38585         if(suppressEvent !== true){
38586             this.fireEvent('check', this, state);
38587         }
38588         this.inSetChecked = true;
38589         this.el.dom.value = state ? this.inputValue : this.valueOff;
38590         this.inSetChecked = false;
38591         
38592     },
38593     // handle setting of hidden value by some other method!!?!?
38594     setFromHidden: function()
38595     {
38596         if(!this.el){
38597             return;
38598         }
38599         //console.log("SET FROM HIDDEN");
38600         //alert('setFrom hidden');
38601         this.setValue(this.el.dom.value);
38602     },
38603     
38604     onDestroy : function()
38605     {
38606         if(this.viewEl){
38607             Roo.get(this.viewEl).remove();
38608         }
38609          
38610         Roo.form.Checkbox.superclass.onDestroy.call(this);
38611     }
38612
38613 });/*
38614  * Based on:
38615  * Ext JS Library 1.1.1
38616  * Copyright(c) 2006-2007, Ext JS, LLC.
38617  *
38618  * Originally Released Under LGPL - original licence link has changed is not relivant.
38619  *
38620  * Fork - LGPL
38621  * <script type="text/javascript">
38622  */
38623  
38624 /**
38625  * @class Roo.form.Radio
38626  * @extends Roo.form.Checkbox
38627  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38628  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38629  * @constructor
38630  * Creates a new Radio
38631  * @param {Object} config Configuration options
38632  */
38633 Roo.form.Radio = function(){
38634     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38635 };
38636 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38637     inputType: 'radio',
38638
38639     /**
38640      * If this radio is part of a group, it will return the selected value
38641      * @return {String}
38642      */
38643     getGroupValue : function(){
38644         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38645     }
38646 });//<script type="text/javascript">
38647
38648 /*
38649  * Ext JS Library 1.1.1
38650  * Copyright(c) 2006-2007, Ext JS, LLC.
38651  * licensing@extjs.com
38652  * 
38653  * http://www.extjs.com/license
38654  */
38655  
38656  /*
38657   * 
38658   * Known bugs:
38659   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38660   * - IE ? - no idea how much works there.
38661   * 
38662   * 
38663   * 
38664   */
38665  
38666
38667 /**
38668  * @class Ext.form.HtmlEditor
38669  * @extends Ext.form.Field
38670  * Provides a lightweight HTML Editor component.
38671  *
38672  * This has been tested on Fireforx / Chrome.. IE may not be so great..
38673  * 
38674  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38675  * supported by this editor.</b><br/><br/>
38676  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38677  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38678  */
38679 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38680       /**
38681      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38682      */
38683     toolbars : false,
38684     /**
38685      * @cfg {String} createLinkText The default text for the create link prompt
38686      */
38687     createLinkText : 'Please enter the URL for the link:',
38688     /**
38689      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38690      */
38691     defaultLinkValue : 'http:/'+'/',
38692    
38693      /**
38694      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38695      *                        Roo.resizable.
38696      */
38697     resizable : false,
38698      /**
38699      * @cfg {Number} height (in pixels)
38700      */   
38701     height: 300,
38702    /**
38703      * @cfg {Number} width (in pixels)
38704      */   
38705     width: 500,
38706     
38707     /**
38708      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38709      * 
38710      */
38711     stylesheets: false,
38712     
38713     // id of frame..
38714     frameId: false,
38715     
38716     // private properties
38717     validationEvent : false,
38718     deferHeight: true,
38719     initialized : false,
38720     activated : false,
38721     sourceEditMode : false,
38722     onFocus : Roo.emptyFn,
38723     iframePad:3,
38724     hideMode:'offsets',
38725     
38726     defaultAutoCreate : { // modified by initCompnoent..
38727         tag: "textarea",
38728         style:"width:500px;height:300px;",
38729         autocomplete: "off"
38730     },
38731
38732     // private
38733     initComponent : function(){
38734         this.addEvents({
38735             /**
38736              * @event initialize
38737              * Fires when the editor is fully initialized (including the iframe)
38738              * @param {HtmlEditor} this
38739              */
38740             initialize: true,
38741             /**
38742              * @event activate
38743              * Fires when the editor is first receives the focus. Any insertion must wait
38744              * until after this event.
38745              * @param {HtmlEditor} this
38746              */
38747             activate: true,
38748              /**
38749              * @event beforesync
38750              * Fires before the textarea is updated with content from the editor iframe. Return false
38751              * to cancel the sync.
38752              * @param {HtmlEditor} this
38753              * @param {String} html
38754              */
38755             beforesync: true,
38756              /**
38757              * @event beforepush
38758              * Fires before the iframe editor is updated with content from the textarea. Return false
38759              * to cancel the push.
38760              * @param {HtmlEditor} this
38761              * @param {String} html
38762              */
38763             beforepush: true,
38764              /**
38765              * @event sync
38766              * Fires when the textarea is updated with content from the editor iframe.
38767              * @param {HtmlEditor} this
38768              * @param {String} html
38769              */
38770             sync: true,
38771              /**
38772              * @event push
38773              * Fires when the iframe editor is updated with content from the textarea.
38774              * @param {HtmlEditor} this
38775              * @param {String} html
38776              */
38777             push: true,
38778              /**
38779              * @event editmodechange
38780              * Fires when the editor switches edit modes
38781              * @param {HtmlEditor} this
38782              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38783              */
38784             editmodechange: true,
38785             /**
38786              * @event editorevent
38787              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38788              * @param {HtmlEditor} this
38789              */
38790             editorevent: true
38791         });
38792         this.defaultAutoCreate =  {
38793             tag: "textarea",
38794             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38795             autocomplete: "off"
38796         };
38797     },
38798
38799     /**
38800      * Protected method that will not generally be called directly. It
38801      * is called when the editor creates its toolbar. Override this method if you need to
38802      * add custom toolbar buttons.
38803      * @param {HtmlEditor} editor
38804      */
38805     createToolbar : function(editor){
38806         if (!editor.toolbars || !editor.toolbars.length) {
38807             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38808         }
38809         
38810         for (var i =0 ; i < editor.toolbars.length;i++) {
38811             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38812             editor.toolbars[i].init(editor);
38813         }
38814          
38815         
38816     },
38817
38818     /**
38819      * Protected method that will not generally be called directly. It
38820      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38821      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38822      */
38823     getDocMarkup : function(){
38824         // body styles..
38825         var st = '';
38826         if (this.stylesheets === false) {
38827             
38828             Roo.get(document.head).select('style').each(function(node) {
38829                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38830             });
38831             
38832             Roo.get(document.head).select('link').each(function(node) { 
38833                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38834             });
38835             
38836         } else if (!this.stylesheets.length) {
38837                 // simple..
38838                 st = '<style type="text/css">' +
38839                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38840                    '</style>';
38841         } else {
38842             Roo.each(this.stylesheets, function(s) {
38843                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38844             });
38845             
38846         }
38847         
38848         st +=  '<style type="text/css">' +
38849             'IMG { cursor: pointer } ' +
38850         '</style>';
38851
38852         
38853         return '<html><head>' + st  +
38854             //<style type="text/css">' +
38855             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38856             //'</style>' +
38857             ' </head><body></body></html>';
38858     },
38859
38860     // private
38861     onRender : function(ct, position)
38862     {
38863         var _t = this;
38864         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38865         this.el.dom.style.border = '0 none';
38866         this.el.dom.setAttribute('tabIndex', -1);
38867         this.el.addClass('x-hidden');
38868         if(Roo.isIE){ // fix IE 1px bogus margin
38869             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38870         }
38871         this.wrap = this.el.wrap({
38872             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38873         });
38874         
38875         if (this.resizable) {
38876             this.resizeEl = new Roo.Resizable(this.wrap, {
38877                 pinned : true,
38878                 wrap: true,
38879                 dynamic : true,
38880                 minHeight : this.height,
38881                 height: this.height,
38882                 handles : this.resizable,
38883                 width: this.width,
38884                 listeners : {
38885                     resize : function(r, w, h) {
38886                         _t.onResize(w,h); // -something
38887                     }
38888                 }
38889             });
38890             
38891         }
38892
38893         this.frameId = Roo.id();
38894         
38895         this.createToolbar(this);
38896         
38897       
38898         
38899         var iframe = this.wrap.createChild({
38900             tag: 'iframe',
38901             id: this.frameId,
38902             name: this.frameId,
38903             frameBorder : 'no',
38904             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38905         }, this.el
38906         );
38907         
38908        // console.log(iframe);
38909         //this.wrap.dom.appendChild(iframe);
38910
38911         this.iframe = iframe.dom;
38912
38913          this.assignDocWin();
38914         
38915         this.doc.designMode = 'on';
38916        
38917         this.doc.open();
38918         this.doc.write(this.getDocMarkup());
38919         this.doc.close();
38920
38921         
38922         var task = { // must defer to wait for browser to be ready
38923             run : function(){
38924                 //console.log("run task?" + this.doc.readyState);
38925                 this.assignDocWin();
38926                 if(this.doc.body || this.doc.readyState == 'complete'){
38927                     try {
38928                         this.doc.designMode="on";
38929                     } catch (e) {
38930                         return;
38931                     }
38932                     Roo.TaskMgr.stop(task);
38933                     this.initEditor.defer(10, this);
38934                 }
38935             },
38936             interval : 10,
38937             duration:10000,
38938             scope: this
38939         };
38940         Roo.TaskMgr.start(task);
38941
38942         if(!this.width){
38943             this.setSize(this.wrap.getSize());
38944         }
38945         if (this.resizeEl) {
38946             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38947             // should trigger onReize..
38948         }
38949     },
38950
38951     // private
38952     onResize : function(w, h)
38953     {
38954         //Roo.log('resize: ' +w + ',' + h );
38955         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38956         if(this.el && this.iframe){
38957             if(typeof w == 'number'){
38958                 var aw = w - this.wrap.getFrameWidth('lr');
38959                 this.el.setWidth(this.adjustWidth('textarea', aw));
38960                 this.iframe.style.width = aw + 'px';
38961             }
38962             if(typeof h == 'number'){
38963                 var tbh = 0;
38964                 for (var i =0; i < this.toolbars.length;i++) {
38965                     // fixme - ask toolbars for heights?
38966                     tbh += this.toolbars[i].tb.el.getHeight();
38967                     if (this.toolbars[i].footer) {
38968                         tbh += this.toolbars[i].footer.el.getHeight();
38969                     }
38970                 }
38971                 
38972                 
38973                 
38974                 
38975                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38976                 ah -= 5; // knock a few pixes off for look..
38977                 this.el.setHeight(this.adjustWidth('textarea', ah));
38978                 this.iframe.style.height = ah + 'px';
38979                 if(this.doc){
38980                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38981                 }
38982             }
38983         }
38984     },
38985
38986     /**
38987      * Toggles the editor between standard and source edit mode.
38988      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38989      */
38990     toggleSourceEdit : function(sourceEditMode){
38991         
38992         this.sourceEditMode = sourceEditMode === true;
38993         
38994         if(this.sourceEditMode){
38995           
38996             this.syncValue();
38997             this.iframe.className = 'x-hidden';
38998             this.el.removeClass('x-hidden');
38999             this.el.dom.removeAttribute('tabIndex');
39000             this.el.focus();
39001         }else{
39002              
39003             this.pushValue();
39004             this.iframe.className = '';
39005             this.el.addClass('x-hidden');
39006             this.el.dom.setAttribute('tabIndex', -1);
39007             this.deferFocus();
39008         }
39009         this.setSize(this.wrap.getSize());
39010         this.fireEvent('editmodechange', this, this.sourceEditMode);
39011     },
39012
39013     // private used internally
39014     createLink : function(){
39015         var url = prompt(this.createLinkText, this.defaultLinkValue);
39016         if(url && url != 'http:/'+'/'){
39017             this.relayCmd('createlink', url);
39018         }
39019     },
39020
39021     // private (for BoxComponent)
39022     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39023
39024     // private (for BoxComponent)
39025     getResizeEl : function(){
39026         return this.wrap;
39027     },
39028
39029     // private (for BoxComponent)
39030     getPositionEl : function(){
39031         return this.wrap;
39032     },
39033
39034     // private
39035     initEvents : function(){
39036         this.originalValue = this.getValue();
39037     },
39038
39039     /**
39040      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39041      * @method
39042      */
39043     markInvalid : Roo.emptyFn,
39044     /**
39045      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39046      * @method
39047      */
39048     clearInvalid : Roo.emptyFn,
39049
39050     setValue : function(v){
39051         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
39052         this.pushValue();
39053     },
39054
39055     /**
39056      * Protected method that will not generally be called directly. If you need/want
39057      * custom HTML cleanup, this is the method you should override.
39058      * @param {String} html The HTML to be cleaned
39059      * return {String} The cleaned HTML
39060      */
39061     cleanHtml : function(html){
39062         html = String(html);
39063         if(html.length > 5){
39064             if(Roo.isSafari){ // strip safari nonsense
39065                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39066             }
39067         }
39068         if(html == '&nbsp;'){
39069             html = '';
39070         }
39071         return html;
39072     },
39073
39074     /**
39075      * Protected method that will not generally be called directly. Syncs the contents
39076      * of the editor iframe with the textarea.
39077      */
39078     syncValue : function(){
39079         if(this.initialized){
39080             var bd = (this.doc.body || this.doc.documentElement);
39081             //this.cleanUpPaste(); -- this is done else where and causes havoc..
39082             var html = bd.innerHTML;
39083             if(Roo.isSafari){
39084                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39085                 var m = bs.match(/text-align:(.*?);/i);
39086                 if(m && m[1]){
39087                     html = '<div style="'+m[0]+'">' + html + '</div>';
39088                 }
39089             }
39090             html = this.cleanHtml(html);
39091             // fix up the special chars..
39092             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
39093                 return "&#"+b.charCodeAt()+";" 
39094             });
39095             if(this.fireEvent('beforesync', this, html) !== false){
39096                 this.el.dom.value = html;
39097                 this.fireEvent('sync', this, html);
39098             }
39099         }
39100     },
39101
39102     /**
39103      * Protected method that will not generally be called directly. Pushes the value of the textarea
39104      * into the iframe editor.
39105      */
39106     pushValue : function(){
39107         if(this.initialized){
39108             var v = this.el.dom.value;
39109             if(v.length < 1){
39110                 v = '&#160;';
39111             }
39112             
39113             if(this.fireEvent('beforepush', this, v) !== false){
39114                 var d = (this.doc.body || this.doc.documentElement);
39115                 d.innerHTML = v;
39116                 this.cleanUpPaste();
39117                 this.el.dom.value = d.innerHTML;
39118                 this.fireEvent('push', this, v);
39119             }
39120         }
39121     },
39122
39123     // private
39124     deferFocus : function(){
39125         this.focus.defer(10, this);
39126     },
39127
39128     // doc'ed in Field
39129     focus : function(){
39130         if(this.win && !this.sourceEditMode){
39131             this.win.focus();
39132         }else{
39133             this.el.focus();
39134         }
39135     },
39136     
39137     assignDocWin: function()
39138     {
39139         var iframe = this.iframe;
39140         
39141          if(Roo.isIE){
39142             this.doc = iframe.contentWindow.document;
39143             this.win = iframe.contentWindow;
39144         } else {
39145             if (!Roo.get(this.frameId)) {
39146                 return;
39147             }
39148             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39149             this.win = Roo.get(this.frameId).dom.contentWindow;
39150         }
39151     },
39152     
39153     // private
39154     initEditor : function(){
39155         //console.log("INIT EDITOR");
39156         this.assignDocWin();
39157         
39158         
39159         
39160         this.doc.designMode="on";
39161         this.doc.open();
39162         this.doc.write(this.getDocMarkup());
39163         this.doc.close();
39164         
39165         var dbody = (this.doc.body || this.doc.documentElement);
39166         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39167         // this copies styles from the containing element into thsi one..
39168         // not sure why we need all of this..
39169         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39170         ss['background-attachment'] = 'fixed'; // w3c
39171         dbody.bgProperties = 'fixed'; // ie
39172         Roo.DomHelper.applyStyles(dbody, ss);
39173         Roo.EventManager.on(this.doc, {
39174             //'mousedown': this.onEditorEvent,
39175             'mouseup': this.onEditorEvent,
39176             'dblclick': this.onEditorEvent,
39177             'click': this.onEditorEvent,
39178             'keyup': this.onEditorEvent,
39179             buffer:100,
39180             scope: this
39181         });
39182         if(Roo.isGecko){
39183             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39184         }
39185         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39186             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39187         }
39188         this.initialized = true;
39189
39190         this.fireEvent('initialize', this);
39191         this.pushValue();
39192     },
39193
39194     // private
39195     onDestroy : function(){
39196         
39197         
39198         
39199         if(this.rendered){
39200             
39201             for (var i =0; i < this.toolbars.length;i++) {
39202                 // fixme - ask toolbars for heights?
39203                 this.toolbars[i].onDestroy();
39204             }
39205             
39206             this.wrap.dom.innerHTML = '';
39207             this.wrap.remove();
39208         }
39209     },
39210
39211     // private
39212     onFirstFocus : function(){
39213         
39214         this.assignDocWin();
39215         
39216         
39217         this.activated = true;
39218         for (var i =0; i < this.toolbars.length;i++) {
39219             this.toolbars[i].onFirstFocus();
39220         }
39221        
39222         if(Roo.isGecko){ // prevent silly gecko errors
39223             this.win.focus();
39224             var s = this.win.getSelection();
39225             if(!s.focusNode || s.focusNode.nodeType != 3){
39226                 var r = s.getRangeAt(0);
39227                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39228                 r.collapse(true);
39229                 this.deferFocus();
39230             }
39231             try{
39232                 this.execCmd('useCSS', true);
39233                 this.execCmd('styleWithCSS', false);
39234             }catch(e){}
39235         }
39236         this.fireEvent('activate', this);
39237     },
39238
39239     // private
39240     adjustFont: function(btn){
39241         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39242         //if(Roo.isSafari){ // safari
39243         //    adjust *= 2;
39244        // }
39245         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39246         if(Roo.isSafari){ // safari
39247             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39248             v =  (v < 10) ? 10 : v;
39249             v =  (v > 48) ? 48 : v;
39250             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39251             
39252         }
39253         
39254         
39255         v = Math.max(1, v+adjust);
39256         
39257         this.execCmd('FontSize', v  );
39258     },
39259
39260     onEditorEvent : function(e){
39261         this.fireEvent('editorevent', this, e);
39262       //  this.updateToolbar();
39263         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
39264     },
39265
39266     insertTag : function(tg)
39267     {
39268         // could be a bit smarter... -> wrap the current selected tRoo..
39269         
39270         this.execCmd("formatblock",   tg);
39271         
39272     },
39273     
39274     insertText : function(txt)
39275     {
39276         
39277         
39278         range = this.createRange();
39279         range.deleteContents();
39280                //alert(Sender.getAttribute('label'));
39281                
39282         range.insertNode(this.doc.createTextNode(txt));
39283     } ,
39284     
39285     // private
39286     relayBtnCmd : function(btn){
39287         this.relayCmd(btn.cmd);
39288     },
39289
39290     /**
39291      * Executes a Midas editor command on the editor document and performs necessary focus and
39292      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39293      * @param {String} cmd The Midas command
39294      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39295      */
39296     relayCmd : function(cmd, value){
39297         this.win.focus();
39298         this.execCmd(cmd, value);
39299         this.fireEvent('editorevent', this);
39300         //this.updateToolbar();
39301         this.deferFocus();
39302     },
39303
39304     /**
39305      * Executes a Midas editor command directly on the editor document.
39306      * For visual commands, you should use {@link #relayCmd} instead.
39307      * <b>This should only be called after the editor is initialized.</b>
39308      * @param {String} cmd The Midas command
39309      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39310      */
39311     execCmd : function(cmd, value){
39312         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39313         this.syncValue();
39314     },
39315  
39316  
39317    
39318     /**
39319      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39320      * to insert tRoo.
39321      * @param {String} text | dom node.. 
39322      */
39323     insertAtCursor : function(text)
39324     {
39325         
39326         
39327         
39328         if(!this.activated){
39329             return;
39330         }
39331         /*
39332         if(Roo.isIE){
39333             this.win.focus();
39334             var r = this.doc.selection.createRange();
39335             if(r){
39336                 r.collapse(true);
39337                 r.pasteHTML(text);
39338                 this.syncValue();
39339                 this.deferFocus();
39340             
39341             }
39342             return;
39343         }
39344         */
39345         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39346             this.win.focus();
39347             
39348             
39349             // from jquery ui (MIT licenced)
39350             var range, node;
39351             var win = this.win;
39352             
39353             if (win.getSelection && win.getSelection().getRangeAt) {
39354                 range = win.getSelection().getRangeAt(0);
39355                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
39356                 range.insertNode(node);
39357             } else if (win.document.selection && win.document.selection.createRange) {
39358                 // no firefox support
39359                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39360                 win.document.selection.createRange().pasteHTML(txt);
39361             } else {
39362                 // no firefox support
39363                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39364                 this.execCmd('InsertHTML', txt);
39365             } 
39366             
39367             this.syncValue();
39368             
39369             this.deferFocus();
39370         }
39371     },
39372  // private
39373     mozKeyPress : function(e){
39374         if(e.ctrlKey){
39375             var c = e.getCharCode(), cmd;
39376           
39377             if(c > 0){
39378                 c = String.fromCharCode(c).toLowerCase();
39379                 switch(c){
39380                     case 'b':
39381                         cmd = 'bold';
39382                         break;
39383                     case 'i':
39384                         cmd = 'italic';
39385                         break;
39386                     
39387                     case 'u':
39388                         cmd = 'underline';
39389                         break;
39390                     
39391                     case 'v':
39392                         this.cleanUpPaste.defer(100, this);
39393                         return;
39394                         
39395                 }
39396                 if(cmd){
39397                     this.win.focus();
39398                     this.execCmd(cmd);
39399                     this.deferFocus();
39400                     e.preventDefault();
39401                 }
39402                 
39403             }
39404         }
39405     },
39406
39407     // private
39408     fixKeys : function(){ // load time branching for fastest keydown performance
39409         if(Roo.isIE){
39410             return function(e){
39411                 var k = e.getKey(), r;
39412                 if(k == e.TAB){
39413                     e.stopEvent();
39414                     r = this.doc.selection.createRange();
39415                     if(r){
39416                         r.collapse(true);
39417                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39418                         this.deferFocus();
39419                     }
39420                     return;
39421                 }
39422                 
39423                 if(k == e.ENTER){
39424                     r = this.doc.selection.createRange();
39425                     if(r){
39426                         var target = r.parentElement();
39427                         if(!target || target.tagName.toLowerCase() != 'li'){
39428                             e.stopEvent();
39429                             r.pasteHTML('<br />');
39430                             r.collapse(false);
39431                             r.select();
39432                         }
39433                     }
39434                 }
39435                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39436                     this.cleanUpPaste.defer(100, this);
39437                     return;
39438                 }
39439                 
39440                 
39441             };
39442         }else if(Roo.isOpera){
39443             return function(e){
39444                 var k = e.getKey();
39445                 if(k == e.TAB){
39446                     e.stopEvent();
39447                     this.win.focus();
39448                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39449                     this.deferFocus();
39450                 }
39451                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39452                     this.cleanUpPaste.defer(100, this);
39453                     return;
39454                 }
39455                 
39456             };
39457         }else if(Roo.isSafari){
39458             return function(e){
39459                 var k = e.getKey();
39460                 
39461                 if(k == e.TAB){
39462                     e.stopEvent();
39463                     this.execCmd('InsertText','\t');
39464                     this.deferFocus();
39465                     return;
39466                 }
39467                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39468                     this.cleanUpPaste.defer(100, this);
39469                     return;
39470                 }
39471                 
39472              };
39473         }
39474     }(),
39475     
39476     getAllAncestors: function()
39477     {
39478         var p = this.getSelectedNode();
39479         var a = [];
39480         if (!p) {
39481             a.push(p); // push blank onto stack..
39482             p = this.getParentElement();
39483         }
39484         
39485         
39486         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39487             a.push(p);
39488             p = p.parentNode;
39489         }
39490         a.push(this.doc.body);
39491         return a;
39492     },
39493     lastSel : false,
39494     lastSelNode : false,
39495     
39496     
39497     getSelection : function() 
39498     {
39499         this.assignDocWin();
39500         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39501     },
39502     
39503     getSelectedNode: function() 
39504     {
39505         // this may only work on Gecko!!!
39506         
39507         // should we cache this!!!!
39508         
39509         
39510         
39511          
39512         var range = this.createRange(this.getSelection()).cloneRange();
39513         
39514         if (Roo.isIE) {
39515             var parent = range.parentElement();
39516             while (true) {
39517                 var testRange = range.duplicate();
39518                 testRange.moveToElementText(parent);
39519                 if (testRange.inRange(range)) {
39520                     break;
39521                 }
39522                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39523                     break;
39524                 }
39525                 parent = parent.parentElement;
39526             }
39527             return parent;
39528         }
39529         
39530         // is ancestor a text element.
39531         var ac =  range.commonAncestorContainer;
39532         if (ac.nodeType == 3) {
39533             ac = ac.parentNode;
39534         }
39535         
39536         var ar = ac.childNodes;
39537          
39538         var nodes = [];
39539         var other_nodes = [];
39540         var has_other_nodes = false;
39541         for (var i=0;i<ar.length;i++) {
39542             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39543                 continue;
39544             }
39545             // fullly contained node.
39546             
39547             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39548                 nodes.push(ar[i]);
39549                 continue;
39550             }
39551             
39552             // probably selected..
39553             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39554                 other_nodes.push(ar[i]);
39555                 continue;
39556             }
39557             // outer..
39558             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39559                 continue;
39560             }
39561             
39562             
39563             has_other_nodes = true;
39564         }
39565         if (!nodes.length && other_nodes.length) {
39566             nodes= other_nodes;
39567         }
39568         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39569             return false;
39570         }
39571         
39572         return nodes[0];
39573     },
39574     createRange: function(sel)
39575     {
39576         // this has strange effects when using with 
39577         // top toolbar - not sure if it's a great idea.
39578         //this.editor.contentWindow.focus();
39579         if (typeof sel != "undefined") {
39580             try {
39581                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39582             } catch(e) {
39583                 return this.doc.createRange();
39584             }
39585         } else {
39586             return this.doc.createRange();
39587         }
39588     },
39589     getParentElement: function()
39590     {
39591         
39592         this.assignDocWin();
39593         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39594         
39595         var range = this.createRange(sel);
39596          
39597         try {
39598             var p = range.commonAncestorContainer;
39599             while (p.nodeType == 3) { // text node
39600                 p = p.parentNode;
39601             }
39602             return p;
39603         } catch (e) {
39604             return null;
39605         }
39606     
39607     },
39608     /***
39609      *
39610      * Range intersection.. the hard stuff...
39611      *  '-1' = before
39612      *  '0' = hits..
39613      *  '1' = after.
39614      *         [ -- selected range --- ]
39615      *   [fail]                        [fail]
39616      *
39617      *    basically..
39618      *      if end is before start or  hits it. fail.
39619      *      if start is after end or hits it fail.
39620      *
39621      *   if either hits (but other is outside. - then it's not 
39622      *   
39623      *    
39624      **/
39625     
39626     
39627     // @see http://www.thismuchiknow.co.uk/?p=64.
39628     rangeIntersectsNode : function(range, node)
39629     {
39630         var nodeRange = node.ownerDocument.createRange();
39631         try {
39632             nodeRange.selectNode(node);
39633         } catch (e) {
39634             nodeRange.selectNodeContents(node);
39635         }
39636     
39637         var rangeStartRange = range.cloneRange();
39638         rangeStartRange.collapse(true);
39639     
39640         var rangeEndRange = range.cloneRange();
39641         rangeEndRange.collapse(false);
39642     
39643         var nodeStartRange = nodeRange.cloneRange();
39644         nodeStartRange.collapse(true);
39645     
39646         var nodeEndRange = nodeRange.cloneRange();
39647         nodeEndRange.collapse(false);
39648     
39649         return rangeStartRange.compareBoundaryPoints(
39650                  Range.START_TO_START, nodeEndRange) == -1 &&
39651                rangeEndRange.compareBoundaryPoints(
39652                  Range.START_TO_START, nodeStartRange) == 1;
39653         
39654          
39655     },
39656     rangeCompareNode : function(range, node)
39657     {
39658         var nodeRange = node.ownerDocument.createRange();
39659         try {
39660             nodeRange.selectNode(node);
39661         } catch (e) {
39662             nodeRange.selectNodeContents(node);
39663         }
39664         
39665         
39666         range.collapse(true);
39667     
39668         nodeRange.collapse(true);
39669      
39670         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39671         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39672          
39673         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39674         
39675         var nodeIsBefore   =  ss == 1;
39676         var nodeIsAfter    = ee == -1;
39677         
39678         if (nodeIsBefore && nodeIsAfter)
39679             return 0; // outer
39680         if (!nodeIsBefore && nodeIsAfter)
39681             return 1; //right trailed.
39682         
39683         if (nodeIsBefore && !nodeIsAfter)
39684             return 2;  // left trailed.
39685         // fully contined.
39686         return 3;
39687     },
39688
39689     // private? - in a new class?
39690     cleanUpPaste :  function()
39691     {
39692         // cleans up the whole document..
39693          Roo.log('cleanuppaste');
39694         this.cleanUpChildren(this.doc.body);
39695         var clean = this.cleanWordChars(this.doc.body.innerHTML);
39696         if (clean != this.doc.body.innerHTML) {
39697             this.doc.body.innerHTML = clean;
39698         }
39699         
39700     },
39701     
39702     cleanWordChars : function(input) {
39703         var he = Roo.form.HtmlEditor;
39704     
39705         var output = input;
39706         Roo.each(he.swapCodes, function(sw) { 
39707         
39708             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39709             output = output.replace(swapper, sw[1]);
39710         });
39711         return output;
39712     },
39713     
39714     
39715     cleanUpChildren : function (n)
39716     {
39717         if (!n.childNodes.length) {
39718             return;
39719         }
39720         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39721            this.cleanUpChild(n.childNodes[i]);
39722         }
39723     },
39724     
39725     
39726         
39727     
39728     cleanUpChild : function (node)
39729     {
39730         //console.log(node);
39731         if (node.nodeName == "#text") {
39732             // clean up silly Windows -- stuff?
39733             return; 
39734         }
39735         if (node.nodeName == "#comment") {
39736             node.parentNode.removeChild(node);
39737             // clean up silly Windows -- stuff?
39738             return; 
39739         }
39740         
39741         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39742             // remove node.
39743             node.parentNode.removeChild(node);
39744             return;
39745             
39746         }
39747         
39748         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39749         
39750         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39751         
39752         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39753             remove_keep_children = true;
39754         }
39755         
39756         if (remove_keep_children) {
39757             this.cleanUpChildren(node);
39758             // inserts everything just before this node...
39759             while (node.childNodes.length) {
39760                 var cn = node.childNodes[0];
39761                 node.removeChild(cn);
39762                 node.parentNode.insertBefore(cn, node);
39763             }
39764             node.parentNode.removeChild(node);
39765             return;
39766         }
39767         
39768         if (!node.attributes || !node.attributes.length) {
39769             this.cleanUpChildren(node);
39770             return;
39771         }
39772         
39773         function cleanAttr(n,v)
39774         {
39775             
39776             if (v.match(/^\./) || v.match(/^\//)) {
39777                 return;
39778             }
39779             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39780                 return;
39781             }
39782             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39783             node.removeAttribute(n);
39784             
39785         }
39786         
39787         function cleanStyle(n,v)
39788         {
39789             if (v.match(/expression/)) { //XSS?? should we even bother..
39790                 node.removeAttribute(n);
39791                 return;
39792             }
39793             
39794             
39795             var parts = v.split(/;/);
39796             Roo.each(parts, function(p) {
39797                 p = p.replace(/\s+/g,'');
39798                 if (!p.length) {
39799                     return true;
39800                 }
39801                 var l = p.split(':').shift().replace(/\s+/g,'');
39802                 
39803                 // only allow 'c whitelisted system attributes'
39804                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39805                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39806                     node.removeAttribute(n);
39807                     return false;
39808                 }
39809                 return true;
39810             });
39811             
39812             
39813         }
39814         
39815         
39816         for (var i = node.attributes.length-1; i > -1 ; i--) {
39817             var a = node.attributes[i];
39818             //console.log(a);
39819             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39820                 node.removeAttribute(a.name);
39821                 return;
39822             }
39823             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39824                 cleanAttr(a.name,a.value); // fixme..
39825                 return;
39826             }
39827             if (a.name == 'style') {
39828                 cleanStyle(a.name,a.value);
39829             }
39830             /// clean up MS crap..
39831             // tecnically this should be a list of valid class'es..
39832             
39833             
39834             if (a.name == 'class') {
39835                 if (a.value.match(/^Mso/)) {
39836                     node.className = '';
39837                 }
39838                 
39839                 if (a.value.match(/body/)) {
39840                     node.className = '';
39841                 }
39842             }
39843             
39844             // style cleanup!?
39845             // class cleanup?
39846             
39847         }
39848         
39849         
39850         this.cleanUpChildren(node);
39851         
39852         
39853     }
39854     
39855     
39856     // hide stuff that is not compatible
39857     /**
39858      * @event blur
39859      * @hide
39860      */
39861     /**
39862      * @event change
39863      * @hide
39864      */
39865     /**
39866      * @event focus
39867      * @hide
39868      */
39869     /**
39870      * @event specialkey
39871      * @hide
39872      */
39873     /**
39874      * @cfg {String} fieldClass @hide
39875      */
39876     /**
39877      * @cfg {String} focusClass @hide
39878      */
39879     /**
39880      * @cfg {String} autoCreate @hide
39881      */
39882     /**
39883      * @cfg {String} inputType @hide
39884      */
39885     /**
39886      * @cfg {String} invalidClass @hide
39887      */
39888     /**
39889      * @cfg {String} invalidText @hide
39890      */
39891     /**
39892      * @cfg {String} msgFx @hide
39893      */
39894     /**
39895      * @cfg {String} validateOnBlur @hide
39896      */
39897 });
39898
39899 Roo.form.HtmlEditor.white = [
39900         'area', 'br', 'img', 'input', 'hr', 'wbr',
39901         
39902        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39903        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39904        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39905        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39906        'table',   'ul',         'xmp', 
39907        
39908        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39909       'thead',   'tr', 
39910      
39911       'dir', 'menu', 'ol', 'ul', 'dl',
39912        
39913       'embed',  'object'
39914 ];
39915
39916
39917 Roo.form.HtmlEditor.black = [
39918     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39919         'applet', // 
39920         'base',   'basefont', 'bgsound', 'blink',  'body', 
39921         'frame',  'frameset', 'head',    'html',   'ilayer', 
39922         'iframe', 'layer',  'link',     'meta',    'object',   
39923         'script', 'style' ,'title',  'xml' // clean later..
39924 ];
39925 Roo.form.HtmlEditor.clean = [
39926     'script', 'style', 'title', 'xml'
39927 ];
39928 Roo.form.HtmlEditor.remove = [
39929     'font'
39930 ];
39931 // attributes..
39932
39933 Roo.form.HtmlEditor.ablack = [
39934     'on'
39935 ];
39936     
39937 Roo.form.HtmlEditor.aclean = [ 
39938     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39939 ];
39940
39941 // protocols..
39942 Roo.form.HtmlEditor.pwhite= [
39943         'http',  'https',  'mailto'
39944 ];
39945
39946 // white listed style attributes.
39947 Roo.form.HtmlEditor.cwhite= [
39948         'text-align',
39949         'font-size'
39950 ];
39951
39952
39953 Roo.form.HtmlEditor.swapCodes   =[ 
39954     [    8211, "--" ], 
39955     [    8212, "--" ], 
39956     [    8216,  "'" ],  
39957     [    8217, "'" ],  
39958     [    8220, '"' ],  
39959     [    8221, '"' ],  
39960     [    8226, "*" ],  
39961     [    8230, "..." ]
39962 ]; 
39963
39964     // <script type="text/javascript">
39965 /*
39966  * Based on
39967  * Ext JS Library 1.1.1
39968  * Copyright(c) 2006-2007, Ext JS, LLC.
39969  *  
39970  
39971  */
39972
39973 /**
39974  * @class Roo.form.HtmlEditorToolbar1
39975  * Basic Toolbar
39976  * 
39977  * Usage:
39978  *
39979  new Roo.form.HtmlEditor({
39980     ....
39981     toolbars : [
39982         new Roo.form.HtmlEditorToolbar1({
39983             disable : { fonts: 1 , format: 1, ..., ... , ...],
39984             btns : [ .... ]
39985         })
39986     }
39987      
39988  * 
39989  * @cfg {Object} disable List of elements to disable..
39990  * @cfg {Array} btns List of additional buttons.
39991  * 
39992  * 
39993  * NEEDS Extra CSS? 
39994  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39995  */
39996  
39997 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39998 {
39999     
40000     Roo.apply(this, config);
40001     
40002     // default disabled, based on 'good practice'..
40003     this.disable = this.disable || {};
40004     Roo.applyIf(this.disable, {
40005         fontSize : true,
40006         colors : true,
40007         specialElements : true
40008     });
40009     
40010     
40011     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40012     // dont call parent... till later.
40013 }
40014
40015 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
40016     
40017     tb: false,
40018     
40019     rendered: false,
40020     
40021     editor : false,
40022     /**
40023      * @cfg {Object} disable  List of toolbar elements to disable
40024          
40025      */
40026     disable : false,
40027       /**
40028      * @cfg {Array} fontFamilies An array of available font families
40029      */
40030     fontFamilies : [
40031         'Arial',
40032         'Courier New',
40033         'Tahoma',
40034         'Times New Roman',
40035         'Verdana'
40036     ],
40037     
40038     specialChars : [
40039            "&#169;",
40040           "&#174;",     
40041           "&#8482;",    
40042           "&#163;" ,    
40043          // "&#8212;",    
40044           "&#8230;",    
40045           "&#247;" ,    
40046         //  "&#225;" ,     ?? a acute?
40047            "&#8364;"    , //Euro
40048        //   "&#8220;"    ,
40049         //  "&#8221;"    ,
40050         //  "&#8226;"    ,
40051           "&#176;"  //   , // degrees
40052
40053          // "&#233;"     , // e ecute
40054          // "&#250;"     , // u ecute?
40055     ],
40056     
40057     specialElements : [
40058         {
40059             text: "Insert Table",
40060             xtype: 'MenuItem',
40061             xns : Roo.Menu,
40062             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
40063                 
40064         },
40065         {    
40066             text: "Insert Image",
40067             xtype: 'MenuItem',
40068             xns : Roo.Menu,
40069             ihtml : '<img src="about:blank"/>'
40070             
40071         }
40072         
40073          
40074     ],
40075     
40076     
40077     inputElements : [ 
40078             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
40079             "input:submit", "input:button", "select", "textarea", "label" ],
40080     formats : [
40081         ["p"] ,  
40082         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
40083         ["pre"],[ "code"], 
40084         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
40085     ],
40086      /**
40087      * @cfg {String} defaultFont default font to use.
40088      */
40089     defaultFont: 'tahoma',
40090    
40091     fontSelect : false,
40092     
40093     
40094     formatCombo : false,
40095     
40096     init : function(editor)
40097     {
40098         this.editor = editor;
40099         
40100         
40101         var fid = editor.frameId;
40102         var etb = this;
40103         function btn(id, toggle, handler){
40104             var xid = fid + '-'+ id ;
40105             return {
40106                 id : xid,
40107                 cmd : id,
40108                 cls : 'x-btn-icon x-edit-'+id,
40109                 enableToggle:toggle !== false,
40110                 scope: editor, // was editor...
40111                 handler:handler||editor.relayBtnCmd,
40112                 clickEvent:'mousedown',
40113                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40114                 tabIndex:-1
40115             };
40116         }
40117         
40118         
40119         
40120         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40121         this.tb = tb;
40122          // stop form submits
40123         tb.el.on('click', function(e){
40124             e.preventDefault(); // what does this do?
40125         });
40126
40127         if(!this.disable.font && !Roo.isSafari){
40128             /* why no safari for fonts
40129             editor.fontSelect = tb.el.createChild({
40130                 tag:'select',
40131                 tabIndex: -1,
40132                 cls:'x-font-select',
40133                 html: editor.createFontOptions()
40134             });
40135             editor.fontSelect.on('change', function(){
40136                 var font = editor.fontSelect.dom.value;
40137                 editor.relayCmd('fontname', font);
40138                 editor.deferFocus();
40139             }, editor);
40140             tb.add(
40141                 editor.fontSelect.dom,
40142                 '-'
40143             );
40144             */
40145         };
40146         if(!this.disable.formats){
40147             this.formatCombo = new Roo.form.ComboBox({
40148                 store: new Roo.data.SimpleStore({
40149                     id : 'tag',
40150                     fields: ['tag'],
40151                     data : this.formats // from states.js
40152                 }),
40153                 blockFocus : true,
40154                 //autoCreate : {tag: "div",  size: "20"},
40155                 displayField:'tag',
40156                 typeAhead: false,
40157                 mode: 'local',
40158                 editable : false,
40159                 triggerAction: 'all',
40160                 emptyText:'Add tag',
40161                 selectOnFocus:true,
40162                 width:135,
40163                 listeners : {
40164                     'select': function(c, r, i) {
40165                         editor.insertTag(r.get('tag'));
40166                         editor.focus();
40167                     }
40168                 }
40169
40170             });
40171             tb.addField(this.formatCombo);
40172             
40173         }
40174         
40175         if(!this.disable.format){
40176             tb.add(
40177                 btn('bold'),
40178                 btn('italic'),
40179                 btn('underline')
40180             );
40181         };
40182         if(!this.disable.fontSize){
40183             tb.add(
40184                 '-',
40185                 
40186                 
40187                 btn('increasefontsize', false, editor.adjustFont),
40188                 btn('decreasefontsize', false, editor.adjustFont)
40189             );
40190         };
40191         
40192         
40193         if(!this.disable.colors){
40194             tb.add(
40195                 '-', {
40196                     id:editor.frameId +'-forecolor',
40197                     cls:'x-btn-icon x-edit-forecolor',
40198                     clickEvent:'mousedown',
40199                     tooltip: this.buttonTips['forecolor'] || undefined,
40200                     tabIndex:-1,
40201                     menu : new Roo.menu.ColorMenu({
40202                         allowReselect: true,
40203                         focus: Roo.emptyFn,
40204                         value:'000000',
40205                         plain:true,
40206                         selectHandler: function(cp, color){
40207                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40208                             editor.deferFocus();
40209                         },
40210                         scope: editor,
40211                         clickEvent:'mousedown'
40212                     })
40213                 }, {
40214                     id:editor.frameId +'backcolor',
40215                     cls:'x-btn-icon x-edit-backcolor',
40216                     clickEvent:'mousedown',
40217                     tooltip: this.buttonTips['backcolor'] || undefined,
40218                     tabIndex:-1,
40219                     menu : new Roo.menu.ColorMenu({
40220                         focus: Roo.emptyFn,
40221                         value:'FFFFFF',
40222                         plain:true,
40223                         allowReselect: true,
40224                         selectHandler: function(cp, color){
40225                             if(Roo.isGecko){
40226                                 editor.execCmd('useCSS', false);
40227                                 editor.execCmd('hilitecolor', color);
40228                                 editor.execCmd('useCSS', true);
40229                                 editor.deferFocus();
40230                             }else{
40231                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40232                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40233                                 editor.deferFocus();
40234                             }
40235                         },
40236                         scope:editor,
40237                         clickEvent:'mousedown'
40238                     })
40239                 }
40240             );
40241         };
40242         // now add all the items...
40243         
40244
40245         if(!this.disable.alignments){
40246             tb.add(
40247                 '-',
40248                 btn('justifyleft'),
40249                 btn('justifycenter'),
40250                 btn('justifyright')
40251             );
40252         };
40253
40254         //if(!Roo.isSafari){
40255             if(!this.disable.links){
40256                 tb.add(
40257                     '-',
40258                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40259                 );
40260             };
40261
40262             if(!this.disable.lists){
40263                 tb.add(
40264                     '-',
40265                     btn('insertorderedlist'),
40266                     btn('insertunorderedlist')
40267                 );
40268             }
40269             if(!this.disable.sourceEdit){
40270                 tb.add(
40271                     '-',
40272                     btn('sourceedit', true, function(btn){
40273                         this.toggleSourceEdit(btn.pressed);
40274                     })
40275                 );
40276             }
40277         //}
40278         
40279         var smenu = { };
40280         // special menu.. - needs to be tidied up..
40281         if (!this.disable.special) {
40282             smenu = {
40283                 text: "&#169;",
40284                 cls: 'x-edit-none',
40285                 
40286                 menu : {
40287                     items : []
40288                 }
40289             };
40290             for (var i =0; i < this.specialChars.length; i++) {
40291                 smenu.menu.items.push({
40292                     
40293                     html: this.specialChars[i],
40294                     handler: function(a,b) {
40295                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40296                         //editor.insertAtCursor(a.html);
40297                         
40298                     },
40299                     tabIndex:-1
40300                 });
40301             }
40302             
40303             
40304             tb.add(smenu);
40305             
40306             
40307         }
40308          
40309         if (!this.disable.specialElements) {
40310             var semenu = {
40311                 text: "Other;",
40312                 cls: 'x-edit-none',
40313                 menu : {
40314                     items : []
40315                 }
40316             };
40317             for (var i =0; i < this.specialElements.length; i++) {
40318                 semenu.menu.items.push(
40319                     Roo.apply({ 
40320                         handler: function(a,b) {
40321                             editor.insertAtCursor(this.ihtml);
40322                         }
40323                     }, this.specialElements[i])
40324                 );
40325                     
40326             }
40327             
40328             tb.add(semenu);
40329             
40330             
40331         }
40332          
40333         
40334         if (this.btns) {
40335             for(var i =0; i< this.btns.length;i++) {
40336                 var b = this.btns[i];
40337                 b.cls =  'x-edit-none';
40338                 b.scope = editor;
40339                 tb.add(b);
40340             }
40341         
40342         }
40343         
40344         
40345         
40346         // disable everything...
40347         
40348         this.tb.items.each(function(item){
40349            if(item.id != editor.frameId+ '-sourceedit'){
40350                 item.disable();
40351             }
40352         });
40353         this.rendered = true;
40354         
40355         // the all the btns;
40356         editor.on('editorevent', this.updateToolbar, this);
40357         // other toolbars need to implement this..
40358         //editor.on('editmodechange', this.updateToolbar, this);
40359     },
40360     
40361     
40362     
40363     /**
40364      * Protected method that will not generally be called directly. It triggers
40365      * a toolbar update by reading the markup state of the current selection in the editor.
40366      */
40367     updateToolbar: function(){
40368
40369         if(!this.editor.activated){
40370             this.editor.onFirstFocus();
40371             return;
40372         }
40373
40374         var btns = this.tb.items.map, 
40375             doc = this.editor.doc,
40376             frameId = this.editor.frameId;
40377
40378         if(!this.disable.font && !Roo.isSafari){
40379             /*
40380             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40381             if(name != this.fontSelect.dom.value){
40382                 this.fontSelect.dom.value = name;
40383             }
40384             */
40385         }
40386         if(!this.disable.format){
40387             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40388             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40389             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40390         }
40391         if(!this.disable.alignments){
40392             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40393             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40394             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40395         }
40396         if(!Roo.isSafari && !this.disable.lists){
40397             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40398             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40399         }
40400         
40401         var ans = this.editor.getAllAncestors();
40402         if (this.formatCombo) {
40403             
40404             
40405             var store = this.formatCombo.store;
40406             this.formatCombo.setValue("");
40407             for (var i =0; i < ans.length;i++) {
40408                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40409                     // select it..
40410                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40411                     break;
40412                 }
40413             }
40414         }
40415         
40416         
40417         
40418         // hides menus... - so this cant be on a menu...
40419         Roo.menu.MenuMgr.hideAll();
40420
40421         //this.editorsyncValue();
40422     },
40423    
40424     
40425     createFontOptions : function(){
40426         var buf = [], fs = this.fontFamilies, ff, lc;
40427         for(var i = 0, len = fs.length; i< len; i++){
40428             ff = fs[i];
40429             lc = ff.toLowerCase();
40430             buf.push(
40431                 '<option value="',lc,'" style="font-family:',ff,';"',
40432                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40433                     ff,
40434                 '</option>'
40435             );
40436         }
40437         return buf.join('');
40438     },
40439     
40440     toggleSourceEdit : function(sourceEditMode){
40441         if(sourceEditMode === undefined){
40442             sourceEditMode = !this.sourceEditMode;
40443         }
40444         this.sourceEditMode = sourceEditMode === true;
40445         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40446         // just toggle the button?
40447         if(btn.pressed !== this.editor.sourceEditMode){
40448             btn.toggle(this.editor.sourceEditMode);
40449             return;
40450         }
40451         
40452         if(this.sourceEditMode){
40453             this.tb.items.each(function(item){
40454                 if(item.cmd != 'sourceedit'){
40455                     item.disable();
40456                 }
40457             });
40458           
40459         }else{
40460             if(this.initialized){
40461                 this.tb.items.each(function(item){
40462                     item.enable();
40463                 });
40464             }
40465             
40466         }
40467         // tell the editor that it's been pressed..
40468         this.editor.toggleSourceEdit(sourceEditMode);
40469        
40470     },
40471      /**
40472      * Object collection of toolbar tooltips for the buttons in the editor. The key
40473      * is the command id associated with that button and the value is a valid QuickTips object.
40474      * For example:
40475 <pre><code>
40476 {
40477     bold : {
40478         title: 'Bold (Ctrl+B)',
40479         text: 'Make the selected text bold.',
40480         cls: 'x-html-editor-tip'
40481     },
40482     italic : {
40483         title: 'Italic (Ctrl+I)',
40484         text: 'Make the selected text italic.',
40485         cls: 'x-html-editor-tip'
40486     },
40487     ...
40488 </code></pre>
40489     * @type Object
40490      */
40491     buttonTips : {
40492         bold : {
40493             title: 'Bold (Ctrl+B)',
40494             text: 'Make the selected text bold.',
40495             cls: 'x-html-editor-tip'
40496         },
40497         italic : {
40498             title: 'Italic (Ctrl+I)',
40499             text: 'Make the selected text italic.',
40500             cls: 'x-html-editor-tip'
40501         },
40502         underline : {
40503             title: 'Underline (Ctrl+U)',
40504             text: 'Underline the selected text.',
40505             cls: 'x-html-editor-tip'
40506         },
40507         increasefontsize : {
40508             title: 'Grow Text',
40509             text: 'Increase the font size.',
40510             cls: 'x-html-editor-tip'
40511         },
40512         decreasefontsize : {
40513             title: 'Shrink Text',
40514             text: 'Decrease the font size.',
40515             cls: 'x-html-editor-tip'
40516         },
40517         backcolor : {
40518             title: 'Text Highlight Color',
40519             text: 'Change the background color of the selected text.',
40520             cls: 'x-html-editor-tip'
40521         },
40522         forecolor : {
40523             title: 'Font Color',
40524             text: 'Change the color of the selected text.',
40525             cls: 'x-html-editor-tip'
40526         },
40527         justifyleft : {
40528             title: 'Align Text Left',
40529             text: 'Align text to the left.',
40530             cls: 'x-html-editor-tip'
40531         },
40532         justifycenter : {
40533             title: 'Center Text',
40534             text: 'Center text in the editor.',
40535             cls: 'x-html-editor-tip'
40536         },
40537         justifyright : {
40538             title: 'Align Text Right',
40539             text: 'Align text to the right.',
40540             cls: 'x-html-editor-tip'
40541         },
40542         insertunorderedlist : {
40543             title: 'Bullet List',
40544             text: 'Start a bulleted list.',
40545             cls: 'x-html-editor-tip'
40546         },
40547         insertorderedlist : {
40548             title: 'Numbered List',
40549             text: 'Start a numbered list.',
40550             cls: 'x-html-editor-tip'
40551         },
40552         createlink : {
40553             title: 'Hyperlink',
40554             text: 'Make the selected text a hyperlink.',
40555             cls: 'x-html-editor-tip'
40556         },
40557         sourceedit : {
40558             title: 'Source Edit',
40559             text: 'Switch to source editing mode.',
40560             cls: 'x-html-editor-tip'
40561         }
40562     },
40563     // private
40564     onDestroy : function(){
40565         if(this.rendered){
40566             
40567             this.tb.items.each(function(item){
40568                 if(item.menu){
40569                     item.menu.removeAll();
40570                     if(item.menu.el){
40571                         item.menu.el.destroy();
40572                     }
40573                 }
40574                 item.destroy();
40575             });
40576              
40577         }
40578     },
40579     onFirstFocus: function() {
40580         this.tb.items.each(function(item){
40581            item.enable();
40582         });
40583     }
40584 });
40585
40586
40587
40588
40589 // <script type="text/javascript">
40590 /*
40591  * Based on
40592  * Ext JS Library 1.1.1
40593  * Copyright(c) 2006-2007, Ext JS, LLC.
40594  *  
40595  
40596  */
40597
40598  
40599 /**
40600  * @class Roo.form.HtmlEditor.ToolbarContext
40601  * Context Toolbar
40602  * 
40603  * Usage:
40604  *
40605  new Roo.form.HtmlEditor({
40606     ....
40607     toolbars : [
40608         { xtype: 'ToolbarStandard', styles : {} }
40609         { xtype: 'ToolbarContext', disable : {} }
40610     ]
40611 })
40612
40613      
40614  * 
40615  * @config : {Object} disable List of elements to disable.. (not done yet.)
40616  * @config : {Object} styles  Map of styles available.
40617  * 
40618  */
40619
40620 Roo.form.HtmlEditor.ToolbarContext = function(config)
40621 {
40622     
40623     Roo.apply(this, config);
40624     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40625     // dont call parent... till later.
40626     this.styles = this.styles || {};
40627 }
40628 Roo.form.HtmlEditor.ToolbarContext.types = {
40629     'IMG' : {
40630         width : {
40631             title: "Width",
40632             width: 40
40633         },
40634         height:  {
40635             title: "Height",
40636             width: 40
40637         },
40638         align: {
40639             title: "Align",
40640             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40641             width : 80
40642             
40643         },
40644         border: {
40645             title: "Border",
40646             width: 40
40647         },
40648         alt: {
40649             title: "Alt",
40650             width: 120
40651         },
40652         src : {
40653             title: "Src",
40654             width: 220
40655         }
40656         
40657     },
40658     'A' : {
40659         name : {
40660             title: "Name",
40661             width: 50
40662         },
40663         href:  {
40664             title: "Href",
40665             width: 220
40666         } // border?
40667         
40668     },
40669     'TABLE' : {
40670         rows : {
40671             title: "Rows",
40672             width: 20
40673         },
40674         cols : {
40675             title: "Cols",
40676             width: 20
40677         },
40678         width : {
40679             title: "Width",
40680             width: 40
40681         },
40682         height : {
40683             title: "Height",
40684             width: 40
40685         },
40686         border : {
40687             title: "Border",
40688             width: 20
40689         }
40690     },
40691     'TD' : {
40692         width : {
40693             title: "Width",
40694             width: 40
40695         },
40696         height : {
40697             title: "Height",
40698             width: 40
40699         },   
40700         align: {
40701             title: "Align",
40702             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40703             width: 80
40704         },
40705         valign: {
40706             title: "Valign",
40707             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40708             width: 80
40709         },
40710         colspan: {
40711             title: "Colspan",
40712             width: 20
40713             
40714         }
40715     },
40716     'INPUT' : {
40717         name : {
40718             title: "name",
40719             width: 120
40720         },
40721         value : {
40722             title: "Value",
40723             width: 120
40724         },
40725         width : {
40726             title: "Width",
40727             width: 40
40728         }
40729     },
40730     'LABEL' : {
40731         'for' : {
40732             title: "For",
40733             width: 120
40734         }
40735     },
40736     'TEXTAREA' : {
40737           name : {
40738             title: "name",
40739             width: 120
40740         },
40741         rows : {
40742             title: "Rows",
40743             width: 20
40744         },
40745         cols : {
40746             title: "Cols",
40747             width: 20
40748         }
40749     },
40750     'SELECT' : {
40751         name : {
40752             title: "name",
40753             width: 120
40754         },
40755         selectoptions : {
40756             title: "Options",
40757             width: 200
40758         }
40759     },
40760     
40761     // should we really allow this??
40762     // should this just be 
40763     'BODY' : {
40764         title : {
40765             title: "title",
40766             width: 200,
40767             disabled : true
40768         }
40769     },
40770     '*' : {
40771         // empty..
40772     }
40773 };
40774
40775
40776
40777 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40778     
40779     tb: false,
40780     
40781     rendered: false,
40782     
40783     editor : false,
40784     /**
40785      * @cfg {Object} disable  List of toolbar elements to disable
40786          
40787      */
40788     disable : false,
40789     /**
40790      * @cfg {Object} styles List of styles 
40791      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40792      *
40793      * These must be defined in the page, so they get rendered correctly..
40794      * .headline { }
40795      * TD.underline { }
40796      * 
40797      */
40798     styles : false,
40799     
40800     
40801     
40802     toolbars : false,
40803     
40804     init : function(editor)
40805     {
40806         this.editor = editor;
40807         
40808         
40809         var fid = editor.frameId;
40810         var etb = this;
40811         function btn(id, toggle, handler){
40812             var xid = fid + '-'+ id ;
40813             return {
40814                 id : xid,
40815                 cmd : id,
40816                 cls : 'x-btn-icon x-edit-'+id,
40817                 enableToggle:toggle !== false,
40818                 scope: editor, // was editor...
40819                 handler:handler||editor.relayBtnCmd,
40820                 clickEvent:'mousedown',
40821                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40822                 tabIndex:-1
40823             };
40824         }
40825         // create a new element.
40826         var wdiv = editor.wrap.createChild({
40827                 tag: 'div'
40828             }, editor.wrap.dom.firstChild.nextSibling, true);
40829         
40830         // can we do this more than once??
40831         
40832          // stop form submits
40833       
40834  
40835         // disable everything...
40836         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40837         this.toolbars = {};
40838            
40839         for (var i in  ty) {
40840           
40841             this.toolbars[i] = this.buildToolbar(ty[i],i);
40842         }
40843         this.tb = this.toolbars.BODY;
40844         this.tb.el.show();
40845         this.buildFooter();
40846         this.footer.show();
40847          
40848         this.rendered = true;
40849         
40850         // the all the btns;
40851         editor.on('editorevent', this.updateToolbar, this);
40852         // other toolbars need to implement this..
40853         //editor.on('editmodechange', this.updateToolbar, this);
40854     },
40855     
40856     
40857     
40858     /**
40859      * Protected method that will not generally be called directly. It triggers
40860      * a toolbar update by reading the markup state of the current selection in the editor.
40861      */
40862     updateToolbar: function(editor,ev,sel){
40863
40864         //Roo.log(ev);
40865         // capture mouse up - this is handy for selecting images..
40866         // perhaps should go somewhere else...
40867         if(!this.editor.activated){
40868              this.editor.onFirstFocus();
40869             return;
40870         }
40871         
40872         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
40873         // selectNode - might want to handle IE?
40874         if (ev &&
40875             (ev.type == 'mouseup' || ev.type == 'click' ) &&
40876             ev.target && ev.target.tagName == 'IMG') {
40877             // they have click on an image...
40878             // let's see if we can change the selection...
40879             sel = ev.target;
40880          
40881               var nodeRange = sel.ownerDocument.createRange();
40882             try {
40883                 nodeRange.selectNode(sel);
40884             } catch (e) {
40885                 nodeRange.selectNodeContents(sel);
40886             }
40887             //nodeRange.collapse(true);
40888             var s = editor.win.getSelection();
40889             s.removeAllRanges();
40890             s.addRange(nodeRange);
40891         }  
40892         
40893       
40894         var updateFooter = sel ? false : true;
40895         
40896         
40897         var ans = this.editor.getAllAncestors();
40898         
40899         // pick
40900         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40901         
40902         if (!sel) { 
40903             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40904             sel = sel ? sel : this.editor.doc.body;
40905             sel = sel.tagName.length ? sel : this.editor.doc.body;
40906             
40907         }
40908         // pick a menu that exists..
40909         var tn = sel.tagName.toUpperCase();
40910         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40911         
40912         tn = sel.tagName.toUpperCase();
40913         
40914         var lastSel = this.tb.selectedNode
40915         
40916         this.tb.selectedNode = sel;
40917         
40918         // if current menu does not match..
40919         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40920                 
40921             this.tb.el.hide();
40922             ///console.log("show: " + tn);
40923             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40924             this.tb.el.show();
40925             // update name
40926             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40927             
40928             
40929             // update attributes
40930             if (this.tb.fields) {
40931                 this.tb.fields.each(function(e) {
40932                    e.setValue(sel.getAttribute(e.name));
40933                 });
40934             }
40935             
40936             // update styles
40937             var st = this.tb.fields.item(0);
40938             st.store.removeAll();
40939             var cn = sel.className.split(/\s+/);
40940             
40941             var avs = [];
40942             if (this.styles['*']) {
40943                 
40944                 Roo.each(this.styles['*'], function(v) {
40945                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40946                 });
40947             }
40948             if (this.styles[tn]) { 
40949                 Roo.each(this.styles[tn], function(v) {
40950                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40951                 });
40952             }
40953             
40954             st.store.loadData(avs);
40955             st.collapse();
40956             st.setValue(cn);
40957             
40958             // flag our selected Node.
40959             this.tb.selectedNode = sel;
40960            
40961            
40962             Roo.menu.MenuMgr.hideAll();
40963
40964         }
40965         
40966         if (!updateFooter) {
40967             return;
40968         }
40969         // update the footer
40970         //
40971         var html = '';
40972         
40973         this.footerEls = ans.reverse();
40974         Roo.each(this.footerEls, function(a,i) {
40975             if (!a) { return; }
40976             html += html.length ? ' &gt; '  :  '';
40977             
40978             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40979             
40980         });
40981        
40982         // 
40983         var sz = this.footDisp.up('td').getSize();
40984         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40985         this.footDisp.dom.style.marginLeft = '5px';
40986         
40987         this.footDisp.dom.style.overflow = 'hidden';
40988         
40989         this.footDisp.dom.innerHTML = html;
40990             
40991         //this.editorsyncValue();
40992     },
40993    
40994        
40995     // private
40996     onDestroy : function(){
40997         if(this.rendered){
40998             
40999             this.tb.items.each(function(item){
41000                 if(item.menu){
41001                     item.menu.removeAll();
41002                     if(item.menu.el){
41003                         item.menu.el.destroy();
41004                     }
41005                 }
41006                 item.destroy();
41007             });
41008              
41009         }
41010     },
41011     onFirstFocus: function() {
41012         // need to do this for all the toolbars..
41013         this.tb.items.each(function(item){
41014            item.enable();
41015         });
41016     },
41017     buildToolbar: function(tlist, nm)
41018     {
41019         var editor = this.editor;
41020          // create a new element.
41021         var wdiv = editor.wrap.createChild({
41022                 tag: 'div'
41023             }, editor.wrap.dom.firstChild.nextSibling, true);
41024         
41025        
41026         var tb = new Roo.Toolbar(wdiv);
41027         // add the name..
41028         
41029         tb.add(nm+ ":&nbsp;");
41030         
41031         var styles = [];
41032         for(var i in this.styles) {
41033             styles.push(i);
41034         }
41035         
41036         // styles...
41037         if (styles && styles.length) {
41038             
41039             // this needs a multi-select checkbox...
41040             tb.addField( new Roo.form.ComboBox({
41041                 store: new Roo.data.SimpleStore({
41042                     id : 'val',
41043                     fields: ['val', 'selected'],
41044                     data : [] 
41045                 }),
41046                 name : 'className',
41047                 displayField:'val',
41048                 typeAhead: false,
41049                 mode: 'local',
41050                 editable : false,
41051                 triggerAction: 'all',
41052                 emptyText:'Select Style',
41053                 selectOnFocus:true,
41054                 width: 130,
41055                 listeners : {
41056                     'select': function(c, r, i) {
41057                         // initial support only for on class per el..
41058                         tb.selectedNode.className =  r ? r.get('val') : '';
41059                         editor.syncValue();
41060                     }
41061                 }
41062     
41063             }));
41064         }
41065             
41066         
41067         
41068         for (var i in tlist) {
41069             
41070             var item = tlist[i];
41071             tb.add(item.title + ":&nbsp;");
41072             
41073             
41074             
41075             
41076             if (item.opts) {
41077                 // opts == pulldown..
41078                 tb.addField( new Roo.form.ComboBox({
41079                     store: new Roo.data.SimpleStore({
41080                         id : 'val',
41081                         fields: ['val'],
41082                         data : item.opts  
41083                     }),
41084                     name : i,
41085                     displayField:'val',
41086                     typeAhead: false,
41087                     mode: 'local',
41088                     editable : false,
41089                     triggerAction: 'all',
41090                     emptyText:'Select',
41091                     selectOnFocus:true,
41092                     width: item.width ? item.width  : 130,
41093                     listeners : {
41094                         'select': function(c, r, i) {
41095                             tb.selectedNode.setAttribute(c.name, r.get('val'));
41096                         }
41097                     }
41098
41099                 }));
41100                 continue;
41101                     
41102                  
41103                 
41104                 tb.addField( new Roo.form.TextField({
41105                     name: i,
41106                     width: 100,
41107                     //allowBlank:false,
41108                     value: ''
41109                 }));
41110                 continue;
41111             }
41112             tb.addField( new Roo.form.TextField({
41113                 name: i,
41114                 width: item.width,
41115                 //allowBlank:true,
41116                 value: '',
41117                 listeners: {
41118                     'change' : function(f, nv, ov) {
41119                         tb.selectedNode.setAttribute(f.name, nv);
41120                     }
41121                 }
41122             }));
41123              
41124         }
41125         tb.el.on('click', function(e){
41126             e.preventDefault(); // what does this do?
41127         });
41128         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
41129         tb.el.hide();
41130         tb.name = nm;
41131         // dont need to disable them... as they will get hidden
41132         return tb;
41133          
41134         
41135     },
41136     buildFooter : function()
41137     {
41138         
41139         var fel = this.editor.wrap.createChild();
41140         this.footer = new Roo.Toolbar(fel);
41141         // toolbar has scrolly on left / right?
41142         var footDisp= new Roo.Toolbar.Fill();
41143         var _t = this;
41144         this.footer.add(
41145             {
41146                 text : '&lt;',
41147                 xtype: 'Button',
41148                 handler : function() {
41149                     _t.footDisp.scrollTo('left',0,true)
41150                 }
41151             }
41152         );
41153         this.footer.add( footDisp );
41154         this.footer.add( 
41155             {
41156                 text : '&gt;',
41157                 xtype: 'Button',
41158                 handler : function() {
41159                     // no animation..
41160                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41161                 }
41162             }
41163         );
41164         var fel = Roo.get(footDisp.el);
41165         fel.addClass('x-editor-context');
41166         this.footDispWrap = fel; 
41167         this.footDispWrap.overflow  = 'hidden';
41168         
41169         this.footDisp = fel.createChild();
41170         this.footDispWrap.on('click', this.onContextClick, this)
41171         
41172         
41173     },
41174     onContextClick : function (ev,dom)
41175     {
41176         ev.preventDefault();
41177         var  cn = dom.className;
41178         Roo.log(cn);
41179         if (!cn.match(/x-ed-loc-/)) {
41180             return;
41181         }
41182         var n = cn.split('-').pop();
41183         var ans = this.footerEls;
41184         var sel = ans[n];
41185         
41186          // pick
41187         var range = this.editor.createRange();
41188         
41189         range.selectNodeContents(sel);
41190         //range.selectNode(sel);
41191         
41192         
41193         var selection = this.editor.getSelection();
41194         selection.removeAllRanges();
41195         selection.addRange(range);
41196         
41197         
41198         
41199         this.updateToolbar(null, null, sel);
41200         
41201         
41202     }
41203     
41204     
41205     
41206     
41207     
41208 });
41209
41210
41211
41212
41213
41214 /*
41215  * Based on:
41216  * Ext JS Library 1.1.1
41217  * Copyright(c) 2006-2007, Ext JS, LLC.
41218  *
41219  * Originally Released Under LGPL - original licence link has changed is not relivant.
41220  *
41221  * Fork - LGPL
41222  * <script type="text/javascript">
41223  */
41224  
41225 /**
41226  * @class Roo.form.BasicForm
41227  * @extends Roo.util.Observable
41228  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41229  * @constructor
41230  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41231  * @param {Object} config Configuration options
41232  */
41233 Roo.form.BasicForm = function(el, config){
41234     this.allItems = [];
41235     this.childForms = [];
41236     Roo.apply(this, config);
41237     /*
41238      * The Roo.form.Field items in this form.
41239      * @type MixedCollection
41240      */
41241      
41242      
41243     this.items = new Roo.util.MixedCollection(false, function(o){
41244         return o.id || (o.id = Roo.id());
41245     });
41246     this.addEvents({
41247         /**
41248          * @event beforeaction
41249          * Fires before any action is performed. Return false to cancel the action.
41250          * @param {Form} this
41251          * @param {Action} action The action to be performed
41252          */
41253         beforeaction: true,
41254         /**
41255          * @event actionfailed
41256          * Fires when an action fails.
41257          * @param {Form} this
41258          * @param {Action} action The action that failed
41259          */
41260         actionfailed : true,
41261         /**
41262          * @event actioncomplete
41263          * Fires when an action is completed.
41264          * @param {Form} this
41265          * @param {Action} action The action that completed
41266          */
41267         actioncomplete : true
41268     });
41269     if(el){
41270         this.initEl(el);
41271     }
41272     Roo.form.BasicForm.superclass.constructor.call(this);
41273 };
41274
41275 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41276     /**
41277      * @cfg {String} method
41278      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41279      */
41280     /**
41281      * @cfg {DataReader} reader
41282      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41283      * This is optional as there is built-in support for processing JSON.
41284      */
41285     /**
41286      * @cfg {DataReader} errorReader
41287      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41288      * This is completely optional as there is built-in support for processing JSON.
41289      */
41290     /**
41291      * @cfg {String} url
41292      * The URL to use for form actions if one isn't supplied in the action options.
41293      */
41294     /**
41295      * @cfg {Boolean} fileUpload
41296      * Set to true if this form is a file upload.
41297      */
41298      
41299     /**
41300      * @cfg {Object} baseParams
41301      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41302      */
41303      /**
41304      
41305     /**
41306      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41307      */
41308     timeout: 30,
41309
41310     // private
41311     activeAction : null,
41312
41313     /**
41314      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41315      * or setValues() data instead of when the form was first created.
41316      */
41317     trackResetOnLoad : false,
41318     
41319     
41320     /**
41321      * childForms - used for multi-tab forms
41322      * @type {Array}
41323      */
41324     childForms : false,
41325     
41326     /**
41327      * allItems - full list of fields.
41328      * @type {Array}
41329      */
41330     allItems : false,
41331     
41332     /**
41333      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41334      * element by passing it or its id or mask the form itself by passing in true.
41335      * @type Mixed
41336      */
41337     waitMsgTarget : false,
41338
41339     // private
41340     initEl : function(el){
41341         this.el = Roo.get(el);
41342         this.id = this.el.id || Roo.id();
41343         this.el.on('submit', this.onSubmit, this);
41344         this.el.addClass('x-form');
41345     },
41346
41347     // private
41348     onSubmit : function(e){
41349         e.stopEvent();
41350     },
41351
41352     /**
41353      * Returns true if client-side validation on the form is successful.
41354      * @return Boolean
41355      */
41356     isValid : function(){
41357         var valid = true;
41358         this.items.each(function(f){
41359            if(!f.validate()){
41360                valid = false;
41361            }
41362         });
41363         return valid;
41364     },
41365
41366     /**
41367      * Returns true if any fields in this form have changed since their original load.
41368      * @return Boolean
41369      */
41370     isDirty : function(){
41371         var dirty = false;
41372         this.items.each(function(f){
41373            if(f.isDirty()){
41374                dirty = true;
41375                return false;
41376            }
41377         });
41378         return dirty;
41379     },
41380
41381     /**
41382      * Performs a predefined action (submit or load) or custom actions you define on this form.
41383      * @param {String} actionName The name of the action type
41384      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41385      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41386      * accept other config options):
41387      * <pre>
41388 Property          Type             Description
41389 ----------------  ---------------  ----------------------------------------------------------------------------------
41390 url               String           The url for the action (defaults to the form's url)
41391 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41392 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41393 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41394                                    validate the form on the client (defaults to false)
41395      * </pre>
41396      * @return {BasicForm} this
41397      */
41398     doAction : function(action, options){
41399         if(typeof action == 'string'){
41400             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41401         }
41402         if(this.fireEvent('beforeaction', this, action) !== false){
41403             this.beforeAction(action);
41404             action.run.defer(100, action);
41405         }
41406         return this;
41407     },
41408
41409     /**
41410      * Shortcut to do a submit action.
41411      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41412      * @return {BasicForm} this
41413      */
41414     submit : function(options){
41415         this.doAction('submit', options);
41416         return this;
41417     },
41418
41419     /**
41420      * Shortcut to do a load action.
41421      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41422      * @return {BasicForm} this
41423      */
41424     load : function(options){
41425         this.doAction('load', options);
41426         return this;
41427     },
41428
41429     /**
41430      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41431      * @param {Record} record The record to edit
41432      * @return {BasicForm} this
41433      */
41434     updateRecord : function(record){
41435         record.beginEdit();
41436         var fs = record.fields;
41437         fs.each(function(f){
41438             var field = this.findField(f.name);
41439             if(field){
41440                 record.set(f.name, field.getValue());
41441             }
41442         }, this);
41443         record.endEdit();
41444         return this;
41445     },
41446
41447     /**
41448      * Loads an Roo.data.Record into this form.
41449      * @param {Record} record The record to load
41450      * @return {BasicForm} this
41451      */
41452     loadRecord : function(record){
41453         this.setValues(record.data);
41454         return this;
41455     },
41456
41457     // private
41458     beforeAction : function(action){
41459         var o = action.options;
41460         
41461        
41462         if(this.waitMsgTarget === true){
41463             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41464         }else if(this.waitMsgTarget){
41465             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41466             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41467         }else {
41468             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41469         }
41470          
41471     },
41472
41473     // private
41474     afterAction : function(action, success){
41475         this.activeAction = null;
41476         var o = action.options;
41477         
41478         if(this.waitMsgTarget === true){
41479             this.el.unmask();
41480         }else if(this.waitMsgTarget){
41481             this.waitMsgTarget.unmask();
41482         }else{
41483             Roo.MessageBox.updateProgress(1);
41484             Roo.MessageBox.hide();
41485         }
41486          
41487         if(success){
41488             if(o.reset){
41489                 this.reset();
41490             }
41491             Roo.callback(o.success, o.scope, [this, action]);
41492             this.fireEvent('actioncomplete', this, action);
41493             
41494         }else{
41495             
41496             // failure condition..
41497             // we have a scenario where updates need confirming.
41498             // eg. if a locking scenario exists..
41499             // we look for { errors : { needs_confirm : true }} in the response.
41500             if (typeof(action.result.errors.needs_confirm) != 'undefined') {
41501                 var _t = this;
41502                 Roo.MessageBox.confirm(
41503                     "Change requires confirmation",
41504                     action.result.errorMsg,
41505                     function(r) {
41506                         if (r != 'yes') {
41507                             return;
41508                         }
41509                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
41510                     }
41511                     
41512                 );
41513                 
41514                 
41515                 
41516                 return;
41517             }
41518             
41519             Roo.callback(o.failure, o.scope, [this, action]);
41520             // show an error message if no failed handler is set..
41521             if (!this.hasListener('actionfailed')) {
41522                 Roo.MessageBox.alert("Error",
41523                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41524                         action.result.errorMsg :
41525                         "Saving Failed, please check your entries"
41526                 );
41527             }
41528             
41529             this.fireEvent('actionfailed', this, action);
41530         }
41531         
41532     },
41533
41534     /**
41535      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41536      * @param {String} id The value to search for
41537      * @return Field
41538      */
41539     findField : function(id){
41540         var field = this.items.get(id);
41541         if(!field){
41542             this.items.each(function(f){
41543                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41544                     field = f;
41545                     return false;
41546                 }
41547             });
41548         }
41549         return field || null;
41550     },
41551
41552     /**
41553      * Add a secondary form to this one, 
41554      * Used to provide tabbed forms. One form is primary, with hidden values 
41555      * which mirror the elements from the other forms.
41556      * 
41557      * @param {Roo.form.Form} form to add.
41558      * 
41559      */
41560     addForm : function(form)
41561     {
41562        
41563         if (this.childForms.indexOf(form) > -1) {
41564             // already added..
41565             return;
41566         }
41567         this.childForms.push(form);
41568         var n = '';
41569         Roo.each(form.allItems, function (fe) {
41570             
41571             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41572             if (this.findField(n)) { // already added..
41573                 return;
41574             }
41575             var add = new Roo.form.Hidden({
41576                 name : n
41577             });
41578             add.render(this.el);
41579             
41580             this.add( add );
41581         }, this);
41582         
41583     },
41584     /**
41585      * Mark fields in this form invalid in bulk.
41586      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41587      * @return {BasicForm} this
41588      */
41589     markInvalid : function(errors){
41590         if(errors instanceof Array){
41591             for(var i = 0, len = errors.length; i < len; i++){
41592                 var fieldError = errors[i];
41593                 var f = this.findField(fieldError.id);
41594                 if(f){
41595                     f.markInvalid(fieldError.msg);
41596                 }
41597             }
41598         }else{
41599             var field, id;
41600             for(id in errors){
41601                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41602                     field.markInvalid(errors[id]);
41603                 }
41604             }
41605         }
41606         Roo.each(this.childForms || [], function (f) {
41607             f.markInvalid(errors);
41608         });
41609         
41610         return this;
41611     },
41612
41613     /**
41614      * Set values for fields in this form in bulk.
41615      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41616      * @return {BasicForm} this
41617      */
41618     setValues : function(values){
41619         if(values instanceof Array){ // array of objects
41620             for(var i = 0, len = values.length; i < len; i++){
41621                 var v = values[i];
41622                 var f = this.findField(v.id);
41623                 if(f){
41624                     f.setValue(v.value);
41625                     if(this.trackResetOnLoad){
41626                         f.originalValue = f.getValue();
41627                     }
41628                 }
41629             }
41630         }else{ // object hash
41631             var field, id;
41632             for(id in values){
41633                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41634                     
41635                     if (field.setFromData && 
41636                         field.valueField && 
41637                         field.displayField &&
41638                         // combos' with local stores can 
41639                         // be queried via setValue()
41640                         // to set their value..
41641                         (field.store && !field.store.isLocal)
41642                         ) {
41643                         // it's a combo
41644                         var sd = { };
41645                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41646                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41647                         field.setFromData(sd);
41648                         
41649                     } else {
41650                         field.setValue(values[id]);
41651                     }
41652                     
41653                     
41654                     if(this.trackResetOnLoad){
41655                         field.originalValue = field.getValue();
41656                     }
41657                 }
41658             }
41659         }
41660          
41661         Roo.each(this.childForms || [], function (f) {
41662             f.setValues(values);
41663         });
41664                 
41665         return this;
41666     },
41667
41668     /**
41669      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41670      * they are returned as an array.
41671      * @param {Boolean} asString
41672      * @return {Object}
41673      */
41674     getValues : function(asString){
41675         if (this.childForms) {
41676             // copy values from the child forms
41677             Roo.each(this.childForms, function (f) {
41678                 this.setValues(f.getValues());
41679             }, this);
41680         }
41681         
41682         
41683         
41684         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41685         if(asString === true){
41686             return fs;
41687         }
41688         return Roo.urlDecode(fs);
41689     },
41690     
41691     /**
41692      * Returns the fields in this form as an object with key/value pairs. 
41693      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41694      * @return {Object}
41695      */
41696     getFieldValues : function(with_hidden)
41697     {
41698         if (this.childForms) {
41699             // copy values from the child forms
41700             // should this call getFieldValues - probably not as we do not currently copy
41701             // hidden fields when we generate..
41702             Roo.each(this.childForms, function (f) {
41703                 this.setValues(f.getValues());
41704             }, this);
41705         }
41706         
41707         var ret = {};
41708         this.items.each(function(f){
41709             if (!f.getName()) {
41710                 return;
41711             }
41712             var v = f.getValue();
41713             // not sure if this supported any more..
41714             if ((typeof(v) == 'object') && f.getRawValue) {
41715                 v = f.getRawValue() ; // dates..
41716             }
41717             // combo boxes where name != hiddenName...
41718             if (f.name != f.getName()) {
41719                 ret[f.name] = f.getRawValue();
41720             }
41721             ret[f.getName()] = v;
41722         });
41723         
41724         return ret;
41725     },
41726
41727     /**
41728      * Clears all invalid messages in this form.
41729      * @return {BasicForm} this
41730      */
41731     clearInvalid : function(){
41732         this.items.each(function(f){
41733            f.clearInvalid();
41734         });
41735         
41736         Roo.each(this.childForms || [], function (f) {
41737             f.clearInvalid();
41738         });
41739         
41740         
41741         return this;
41742     },
41743
41744     /**
41745      * Resets this form.
41746      * @return {BasicForm} this
41747      */
41748     reset : function(){
41749         this.items.each(function(f){
41750             f.reset();
41751         });
41752         
41753         Roo.each(this.childForms || [], function (f) {
41754             f.reset();
41755         });
41756        
41757         
41758         return this;
41759     },
41760
41761     /**
41762      * Add Roo.form components to this form.
41763      * @param {Field} field1
41764      * @param {Field} field2 (optional)
41765      * @param {Field} etc (optional)
41766      * @return {BasicForm} this
41767      */
41768     add : function(){
41769         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41770         return this;
41771     },
41772
41773
41774     /**
41775      * Removes a field from the items collection (does NOT remove its markup).
41776      * @param {Field} field
41777      * @return {BasicForm} this
41778      */
41779     remove : function(field){
41780         this.items.remove(field);
41781         return this;
41782     },
41783
41784     /**
41785      * Looks at the fields in this form, checks them for an id attribute,
41786      * and calls applyTo on the existing dom element with that id.
41787      * @return {BasicForm} this
41788      */
41789     render : function(){
41790         this.items.each(function(f){
41791             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41792                 f.applyTo(f.id);
41793             }
41794         });
41795         return this;
41796     },
41797
41798     /**
41799      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41800      * @param {Object} values
41801      * @return {BasicForm} this
41802      */
41803     applyToFields : function(o){
41804         this.items.each(function(f){
41805            Roo.apply(f, o);
41806         });
41807         return this;
41808     },
41809
41810     /**
41811      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41812      * @param {Object} values
41813      * @return {BasicForm} this
41814      */
41815     applyIfToFields : function(o){
41816         this.items.each(function(f){
41817            Roo.applyIf(f, o);
41818         });
41819         return this;
41820     }
41821 });
41822
41823 // back compat
41824 Roo.BasicForm = Roo.form.BasicForm;/*
41825  * Based on:
41826  * Ext JS Library 1.1.1
41827  * Copyright(c) 2006-2007, Ext JS, LLC.
41828  *
41829  * Originally Released Under LGPL - original licence link has changed is not relivant.
41830  *
41831  * Fork - LGPL
41832  * <script type="text/javascript">
41833  */
41834
41835 /**
41836  * @class Roo.form.Form
41837  * @extends Roo.form.BasicForm
41838  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41839  * @constructor
41840  * @param {Object} config Configuration options
41841  */
41842 Roo.form.Form = function(config){
41843     var xitems =  [];
41844     if (config.items) {
41845         xitems = config.items;
41846         delete config.items;
41847     }
41848    
41849     
41850     Roo.form.Form.superclass.constructor.call(this, null, config);
41851     this.url = this.url || this.action;
41852     if(!this.root){
41853         this.root = new Roo.form.Layout(Roo.applyIf({
41854             id: Roo.id()
41855         }, config));
41856     }
41857     this.active = this.root;
41858     /**
41859      * Array of all the buttons that have been added to this form via {@link addButton}
41860      * @type Array
41861      */
41862     this.buttons = [];
41863     this.allItems = [];
41864     this.addEvents({
41865         /**
41866          * @event clientvalidation
41867          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41868          * @param {Form} this
41869          * @param {Boolean} valid true if the form has passed client-side validation
41870          */
41871         clientvalidation: true,
41872         /**
41873          * @event rendered
41874          * Fires when the form is rendered
41875          * @param {Roo.form.Form} form
41876          */
41877         rendered : true
41878     });
41879     
41880     if (this.progressUrl) {
41881             // push a hidden field onto the list of fields..
41882             this.addxtype( {
41883                     xns: Roo.form, 
41884                     xtype : 'Hidden', 
41885                     name : 'UPLOAD_IDENTIFIER' 
41886             });
41887         }
41888         
41889     
41890     Roo.each(xitems, this.addxtype, this);
41891     
41892     
41893     
41894 };
41895
41896 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41897     /**
41898      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41899      */
41900     /**
41901      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41902      */
41903     /**
41904      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41905      */
41906     buttonAlign:'center',
41907
41908     /**
41909      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41910      */
41911     minButtonWidth:75,
41912
41913     /**
41914      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41915      * This property cascades to child containers if not set.
41916      */
41917     labelAlign:'left',
41918
41919     /**
41920      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41921      * fires a looping event with that state. This is required to bind buttons to the valid
41922      * state using the config value formBind:true on the button.
41923      */
41924     monitorValid : false,
41925
41926     /**
41927      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41928      */
41929     monitorPoll : 200,
41930     
41931     /**
41932      * @cfg {String} progressUrl - Url to return progress data 
41933      */
41934     
41935     progressUrl : false,
41936   
41937     /**
41938      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41939      * fields are added and the column is closed. If no fields are passed the column remains open
41940      * until end() is called.
41941      * @param {Object} config The config to pass to the column
41942      * @param {Field} field1 (optional)
41943      * @param {Field} field2 (optional)
41944      * @param {Field} etc (optional)
41945      * @return Column The column container object
41946      */
41947     column : function(c){
41948         var col = new Roo.form.Column(c);
41949         this.start(col);
41950         if(arguments.length > 1){ // duplicate code required because of Opera
41951             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41952             this.end();
41953         }
41954         return col;
41955     },
41956
41957     /**
41958      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41959      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41960      * until end() is called.
41961      * @param {Object} config The config to pass to the fieldset
41962      * @param {Field} field1 (optional)
41963      * @param {Field} field2 (optional)
41964      * @param {Field} etc (optional)
41965      * @return FieldSet The fieldset container object
41966      */
41967     fieldset : function(c){
41968         var fs = new Roo.form.FieldSet(c);
41969         this.start(fs);
41970         if(arguments.length > 1){ // duplicate code required because of Opera
41971             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41972             this.end();
41973         }
41974         return fs;
41975     },
41976
41977     /**
41978      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41979      * fields are added and the container is closed. If no fields are passed the container remains open
41980      * until end() is called.
41981      * @param {Object} config The config to pass to the Layout
41982      * @param {Field} field1 (optional)
41983      * @param {Field} field2 (optional)
41984      * @param {Field} etc (optional)
41985      * @return Layout The container object
41986      */
41987     container : function(c){
41988         var l = new Roo.form.Layout(c);
41989         this.start(l);
41990         if(arguments.length > 1){ // duplicate code required because of Opera
41991             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41992             this.end();
41993         }
41994         return l;
41995     },
41996
41997     /**
41998      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41999      * @param {Object} container A Roo.form.Layout or subclass of Layout
42000      * @return {Form} this
42001      */
42002     start : function(c){
42003         // cascade label info
42004         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
42005         this.active.stack.push(c);
42006         c.ownerCt = this.active;
42007         this.active = c;
42008         return this;
42009     },
42010
42011     /**
42012      * Closes the current open container
42013      * @return {Form} this
42014      */
42015     end : function(){
42016         if(this.active == this.root){
42017             return this;
42018         }
42019         this.active = this.active.ownerCt;
42020         return this;
42021     },
42022
42023     /**
42024      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
42025      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
42026      * as the label of the field.
42027      * @param {Field} field1
42028      * @param {Field} field2 (optional)
42029      * @param {Field} etc. (optional)
42030      * @return {Form} this
42031      */
42032     add : function(){
42033         this.active.stack.push.apply(this.active.stack, arguments);
42034         this.allItems.push.apply(this.allItems,arguments);
42035         var r = [];
42036         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
42037             if(a[i].isFormField){
42038                 r.push(a[i]);
42039             }
42040         }
42041         if(r.length > 0){
42042             Roo.form.Form.superclass.add.apply(this, r);
42043         }
42044         return this;
42045     },
42046     
42047
42048     
42049     
42050     
42051      /**
42052      * Find any element that has been added to a form, using it's ID or name
42053      * This can include framesets, columns etc. along with regular fields..
42054      * @param {String} id - id or name to find.
42055      
42056      * @return {Element} e - or false if nothing found.
42057      */
42058     findbyId : function(id)
42059     {
42060         var ret = false;
42061         if (!id) {
42062             return ret;
42063         }
42064         Roo.each(this.allItems, function(f){
42065             if (f.id == id || f.name == id ){
42066                 ret = f;
42067                 return false;
42068             }
42069         });
42070         return ret;
42071     },
42072
42073     
42074     
42075     /**
42076      * Render this form into the passed container. This should only be called once!
42077      * @param {String/HTMLElement/Element} container The element this component should be rendered into
42078      * @return {Form} this
42079      */
42080     render : function(ct)
42081     {
42082         
42083         
42084         
42085         ct = Roo.get(ct);
42086         var o = this.autoCreate || {
42087             tag: 'form',
42088             method : this.method || 'POST',
42089             id : this.id || Roo.id()
42090         };
42091         this.initEl(ct.createChild(o));
42092
42093         this.root.render(this.el);
42094         
42095        
42096              
42097         this.items.each(function(f){
42098             f.render('x-form-el-'+f.id);
42099         });
42100
42101         if(this.buttons.length > 0){
42102             // tables are required to maintain order and for correct IE layout
42103             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
42104                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
42105                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
42106             }}, null, true);
42107             var tr = tb.getElementsByTagName('tr')[0];
42108             for(var i = 0, len = this.buttons.length; i < len; i++) {
42109                 var b = this.buttons[i];
42110                 var td = document.createElement('td');
42111                 td.className = 'x-form-btn-td';
42112                 b.render(tr.appendChild(td));
42113             }
42114         }
42115         if(this.monitorValid){ // initialize after render
42116             this.startMonitoring();
42117         }
42118         this.fireEvent('rendered', this);
42119         return this;
42120     },
42121
42122     /**
42123      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
42124      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
42125      * object or a valid Roo.DomHelper element config
42126      * @param {Function} handler The function called when the button is clicked
42127      * @param {Object} scope (optional) The scope of the handler function
42128      * @return {Roo.Button}
42129      */
42130     addButton : function(config, handler, scope){
42131         var bc = {
42132             handler: handler,
42133             scope: scope,
42134             minWidth: this.minButtonWidth,
42135             hideParent:true
42136         };
42137         if(typeof config == "string"){
42138             bc.text = config;
42139         }else{
42140             Roo.apply(bc, config);
42141         }
42142         var btn = new Roo.Button(null, bc);
42143         this.buttons.push(btn);
42144         return btn;
42145     },
42146
42147      /**
42148      * Adds a series of form elements (using the xtype property as the factory method.
42149      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
42150      * @param {Object} config 
42151      */
42152     
42153     addxtype : function()
42154     {
42155         var ar = Array.prototype.slice.call(arguments, 0);
42156         var ret = false;
42157         for(var i = 0; i < ar.length; i++) {
42158             if (!ar[i]) {
42159                 continue; // skip -- if this happends something invalid got sent, we 
42160                 // should ignore it, as basically that interface element will not show up
42161                 // and that should be pretty obvious!!
42162             }
42163             
42164             if (Roo.form[ar[i].xtype]) {
42165                 ar[i].form = this;
42166                 var fe = Roo.factory(ar[i], Roo.form);
42167                 if (!ret) {
42168                     ret = fe;
42169                 }
42170                 fe.form = this;
42171                 if (fe.store) {
42172                     fe.store.form = this;
42173                 }
42174                 if (fe.isLayout) {  
42175                          
42176                     this.start(fe);
42177                     this.allItems.push(fe);
42178                     if (fe.items && fe.addxtype) {
42179                         fe.addxtype.apply(fe, fe.items);
42180                         delete fe.items;
42181                     }
42182                      this.end();
42183                     continue;
42184                 }
42185                 
42186                 
42187                  
42188                 this.add(fe);
42189               //  console.log('adding ' + ar[i].xtype);
42190             }
42191             if (ar[i].xtype == 'Button') {  
42192                 //console.log('adding button');
42193                 //console.log(ar[i]);
42194                 this.addButton(ar[i]);
42195                 this.allItems.push(fe);
42196                 continue;
42197             }
42198             
42199             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42200                 alert('end is not supported on xtype any more, use items');
42201             //    this.end();
42202             //    //console.log('adding end');
42203             }
42204             
42205         }
42206         return ret;
42207     },
42208     
42209     /**
42210      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42211      * option "monitorValid"
42212      */
42213     startMonitoring : function(){
42214         if(!this.bound){
42215             this.bound = true;
42216             Roo.TaskMgr.start({
42217                 run : this.bindHandler,
42218                 interval : this.monitorPoll || 200,
42219                 scope: this
42220             });
42221         }
42222     },
42223
42224     /**
42225      * Stops monitoring of the valid state of this form
42226      */
42227     stopMonitoring : function(){
42228         this.bound = false;
42229     },
42230
42231     // private
42232     bindHandler : function(){
42233         if(!this.bound){
42234             return false; // stops binding
42235         }
42236         var valid = true;
42237         this.items.each(function(f){
42238             if(!f.isValid(true)){
42239                 valid = false;
42240                 return false;
42241             }
42242         });
42243         for(var i = 0, len = this.buttons.length; i < len; i++){
42244             var btn = this.buttons[i];
42245             if(btn.formBind === true && btn.disabled === valid){
42246                 btn.setDisabled(!valid);
42247             }
42248         }
42249         this.fireEvent('clientvalidation', this, valid);
42250     }
42251     
42252     
42253     
42254     
42255     
42256     
42257     
42258     
42259 });
42260
42261
42262 // back compat
42263 Roo.Form = Roo.form.Form;
42264 /*
42265  * Based on:
42266  * Ext JS Library 1.1.1
42267  * Copyright(c) 2006-2007, Ext JS, LLC.
42268  *
42269  * Originally Released Under LGPL - original licence link has changed is not relivant.
42270  *
42271  * Fork - LGPL
42272  * <script type="text/javascript">
42273  */
42274  
42275  /**
42276  * @class Roo.form.Action
42277  * Internal Class used to handle form actions
42278  * @constructor
42279  * @param {Roo.form.BasicForm} el The form element or its id
42280  * @param {Object} config Configuration options
42281  */
42282  
42283  
42284 // define the action interface
42285 Roo.form.Action = function(form, options){
42286     this.form = form;
42287     this.options = options || {};
42288 };
42289 /**
42290  * Client Validation Failed
42291  * @const 
42292  */
42293 Roo.form.Action.CLIENT_INVALID = 'client';
42294 /**
42295  * Server Validation Failed
42296  * @const 
42297  */
42298  Roo.form.Action.SERVER_INVALID = 'server';
42299  /**
42300  * Connect to Server Failed
42301  * @const 
42302  */
42303 Roo.form.Action.CONNECT_FAILURE = 'connect';
42304 /**
42305  * Reading Data from Server Failed
42306  * @const 
42307  */
42308 Roo.form.Action.LOAD_FAILURE = 'load';
42309
42310 Roo.form.Action.prototype = {
42311     type : 'default',
42312     failureType : undefined,
42313     response : undefined,
42314     result : undefined,
42315
42316     // interface method
42317     run : function(options){
42318
42319     },
42320
42321     // interface method
42322     success : function(response){
42323
42324     },
42325
42326     // interface method
42327     handleResponse : function(response){
42328
42329     },
42330
42331     // default connection failure
42332     failure : function(response){
42333         
42334         this.response = response;
42335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42336         this.form.afterAction(this, false);
42337     },
42338
42339     processResponse : function(response){
42340         this.response = response;
42341         if(!response.responseText){
42342             return true;
42343         }
42344         this.result = this.handleResponse(response);
42345         return this.result;
42346     },
42347
42348     // utility functions used internally
42349     getUrl : function(appendParams){
42350         var url = this.options.url || this.form.url || this.form.el.dom.action;
42351         if(appendParams){
42352             var p = this.getParams();
42353             if(p){
42354                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42355             }
42356         }
42357         return url;
42358     },
42359
42360     getMethod : function(){
42361         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42362     },
42363
42364     getParams : function(){
42365         var bp = this.form.baseParams;
42366         var p = this.options.params;
42367         if(p){
42368             if(typeof p == "object"){
42369                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42370             }else if(typeof p == 'string' && bp){
42371                 p += '&' + Roo.urlEncode(bp);
42372             }
42373         }else if(bp){
42374             p = Roo.urlEncode(bp);
42375         }
42376         return p;
42377     },
42378
42379     createCallback : function(){
42380         return {
42381             success: this.success,
42382             failure: this.failure,
42383             scope: this,
42384             timeout: (this.form.timeout*1000),
42385             upload: this.form.fileUpload ? this.success : undefined
42386         };
42387     }
42388 };
42389
42390 Roo.form.Action.Submit = function(form, options){
42391     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42392 };
42393
42394 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42395     type : 'submit',
42396
42397     haveProgress : false,
42398     uploadComplete : false,
42399     
42400     // uploadProgress indicator.
42401     uploadProgress : function()
42402     {
42403         if (!this.form.progressUrl) {
42404             return;
42405         }
42406         
42407         if (!this.haveProgress) {
42408             Roo.MessageBox.progress("Uploading", "Uploading");
42409         }
42410         if (this.uploadComplete) {
42411            Roo.MessageBox.hide();
42412            return;
42413         }
42414         
42415         this.haveProgress = true;
42416    
42417         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42418         
42419         var c = new Roo.data.Connection();
42420         c.request({
42421             url : this.form.progressUrl,
42422             params: {
42423                 id : uid
42424             },
42425             method: 'GET',
42426             success : function(req){
42427                //console.log(data);
42428                 var rdata = false;
42429                 var edata;
42430                 try  {
42431                    rdata = Roo.decode(req.responseText)
42432                 } catch (e) {
42433                     Roo.log("Invalid data from server..");
42434                     Roo.log(edata);
42435                     return;
42436                 }
42437                 if (!rdata || !rdata.success) {
42438                     Roo.log(rdata);
42439                     return;
42440                 }
42441                 var data = rdata.data;
42442                 
42443                 if (this.uploadComplete) {
42444                    Roo.MessageBox.hide();
42445                    return;
42446                 }
42447                    
42448                 if (data){
42449                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42450                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42451                     );
42452                 }
42453                 this.uploadProgress.defer(2000,this);
42454             },
42455        
42456             failure: function(data) {
42457                 Roo.log('progress url failed ');
42458                 Roo.log(data);
42459             },
42460             scope : this
42461         });
42462            
42463     },
42464     
42465     
42466     run : function()
42467     {
42468         // run get Values on the form, so it syncs any secondary forms.
42469         this.form.getValues();
42470         
42471         var o = this.options;
42472         var method = this.getMethod();
42473         var isPost = method == 'POST';
42474         if(o.clientValidation === false || this.form.isValid()){
42475             
42476             if (this.form.progressUrl) {
42477                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42478                     (new Date() * 1) + '' + Math.random());
42479                     
42480             } 
42481             
42482             
42483             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42484                 form:this.form.el.dom,
42485                 url:this.getUrl(!isPost),
42486                 method: method,
42487                 params:isPost ? this.getParams() : null,
42488                 isUpload: this.form.fileUpload
42489             }));
42490             
42491             this.uploadProgress();
42492
42493         }else if (o.clientValidation !== false){ // client validation failed
42494             this.failureType = Roo.form.Action.CLIENT_INVALID;
42495             this.form.afterAction(this, false);
42496         }
42497     },
42498
42499     success : function(response)
42500     {
42501         this.uploadComplete= true;
42502         if (this.haveProgress) {
42503             Roo.MessageBox.hide();
42504         }
42505         
42506         
42507         var result = this.processResponse(response);
42508         if(result === true || result.success){
42509             this.form.afterAction(this, true);
42510             return;
42511         }
42512         if(result.errors){
42513             this.form.markInvalid(result.errors);
42514             this.failureType = Roo.form.Action.SERVER_INVALID;
42515         }
42516         this.form.afterAction(this, false);
42517     },
42518     failure : function(response)
42519     {
42520         this.uploadComplete= true;
42521         if (this.haveProgress) {
42522             Roo.MessageBox.hide();
42523         }
42524         
42525         this.response = response;
42526         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42527         this.form.afterAction(this, false);
42528     },
42529     
42530     handleResponse : function(response){
42531         if(this.form.errorReader){
42532             var rs = this.form.errorReader.read(response);
42533             var errors = [];
42534             if(rs.records){
42535                 for(var i = 0, len = rs.records.length; i < len; i++) {
42536                     var r = rs.records[i];
42537                     errors[i] = r.data;
42538                 }
42539             }
42540             if(errors.length < 1){
42541                 errors = null;
42542             }
42543             return {
42544                 success : rs.success,
42545                 errors : errors
42546             };
42547         }
42548         var ret = false;
42549         try {
42550             ret = Roo.decode(response.responseText);
42551         } catch (e) {
42552             ret = {
42553                 success: false,
42554                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42555                 errors : []
42556             };
42557         }
42558         return ret;
42559         
42560     }
42561 });
42562
42563
42564 Roo.form.Action.Load = function(form, options){
42565     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42566     this.reader = this.form.reader;
42567 };
42568
42569 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42570     type : 'load',
42571
42572     run : function(){
42573         
42574         Roo.Ajax.request(Roo.apply(
42575                 this.createCallback(), {
42576                     method:this.getMethod(),
42577                     url:this.getUrl(false),
42578                     params:this.getParams()
42579         }));
42580     },
42581
42582     success : function(response){
42583         
42584         var result = this.processResponse(response);
42585         if(result === true || !result.success || !result.data){
42586             this.failureType = Roo.form.Action.LOAD_FAILURE;
42587             this.form.afterAction(this, false);
42588             return;
42589         }
42590         this.form.clearInvalid();
42591         this.form.setValues(result.data);
42592         this.form.afterAction(this, true);
42593     },
42594
42595     handleResponse : function(response){
42596         if(this.form.reader){
42597             var rs = this.form.reader.read(response);
42598             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42599             return {
42600                 success : rs.success,
42601                 data : data
42602             };
42603         }
42604         return Roo.decode(response.responseText);
42605     }
42606 });
42607
42608 Roo.form.Action.ACTION_TYPES = {
42609     'load' : Roo.form.Action.Load,
42610     'submit' : Roo.form.Action.Submit
42611 };/*
42612  * Based on:
42613  * Ext JS Library 1.1.1
42614  * Copyright(c) 2006-2007, Ext JS, LLC.
42615  *
42616  * Originally Released Under LGPL - original licence link has changed is not relivant.
42617  *
42618  * Fork - LGPL
42619  * <script type="text/javascript">
42620  */
42621  
42622 /**
42623  * @class Roo.form.Layout
42624  * @extends Roo.Component
42625  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42626  * @constructor
42627  * @param {Object} config Configuration options
42628  */
42629 Roo.form.Layout = function(config){
42630     var xitems = [];
42631     if (config.items) {
42632         xitems = config.items;
42633         delete config.items;
42634     }
42635     Roo.form.Layout.superclass.constructor.call(this, config);
42636     this.stack = [];
42637     Roo.each(xitems, this.addxtype, this);
42638      
42639 };
42640
42641 Roo.extend(Roo.form.Layout, Roo.Component, {
42642     /**
42643      * @cfg {String/Object} autoCreate
42644      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42645      */
42646     /**
42647      * @cfg {String/Object/Function} style
42648      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42649      * a function which returns such a specification.
42650      */
42651     /**
42652      * @cfg {String} labelAlign
42653      * Valid values are "left," "top" and "right" (defaults to "left")
42654      */
42655     /**
42656      * @cfg {Number} labelWidth
42657      * Fixed width in pixels of all field labels (defaults to undefined)
42658      */
42659     /**
42660      * @cfg {Boolean} clear
42661      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42662      */
42663     clear : true,
42664     /**
42665      * @cfg {String} labelSeparator
42666      * The separator to use after field labels (defaults to ':')
42667      */
42668     labelSeparator : ':',
42669     /**
42670      * @cfg {Boolean} hideLabels
42671      * True to suppress the display of field labels in this layout (defaults to false)
42672      */
42673     hideLabels : false,
42674
42675     // private
42676     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42677     
42678     isLayout : true,
42679     
42680     // private
42681     onRender : function(ct, position){
42682         if(this.el){ // from markup
42683             this.el = Roo.get(this.el);
42684         }else {  // generate
42685             var cfg = this.getAutoCreate();
42686             this.el = ct.createChild(cfg, position);
42687         }
42688         if(this.style){
42689             this.el.applyStyles(this.style);
42690         }
42691         if(this.labelAlign){
42692             this.el.addClass('x-form-label-'+this.labelAlign);
42693         }
42694         if(this.hideLabels){
42695             this.labelStyle = "display:none";
42696             this.elementStyle = "padding-left:0;";
42697         }else{
42698             if(typeof this.labelWidth == 'number'){
42699                 this.labelStyle = "width:"+this.labelWidth+"px;";
42700                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42701             }
42702             if(this.labelAlign == 'top'){
42703                 this.labelStyle = "width:auto;";
42704                 this.elementStyle = "padding-left:0;";
42705             }
42706         }
42707         var stack = this.stack;
42708         var slen = stack.length;
42709         if(slen > 0){
42710             if(!this.fieldTpl){
42711                 var t = new Roo.Template(
42712                     '<div class="x-form-item {5}">',
42713                         '<label for="{0}" style="{2}">{1}{4}</label>',
42714                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42715                         '</div>',
42716                     '</div><div class="x-form-clear-left"></div>'
42717                 );
42718                 t.disableFormats = true;
42719                 t.compile();
42720                 Roo.form.Layout.prototype.fieldTpl = t;
42721             }
42722             for(var i = 0; i < slen; i++) {
42723                 if(stack[i].isFormField){
42724                     this.renderField(stack[i]);
42725                 }else{
42726                     this.renderComponent(stack[i]);
42727                 }
42728             }
42729         }
42730         if(this.clear){
42731             this.el.createChild({cls:'x-form-clear'});
42732         }
42733     },
42734
42735     // private
42736     renderField : function(f){
42737         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42738                f.id, //0
42739                f.fieldLabel, //1
42740                f.labelStyle||this.labelStyle||'', //2
42741                this.elementStyle||'', //3
42742                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42743                f.itemCls||this.itemCls||''  //5
42744        ], true).getPrevSibling());
42745     },
42746
42747     // private
42748     renderComponent : function(c){
42749         c.render(c.isLayout ? this.el : this.el.createChild());    
42750     },
42751     /**
42752      * Adds a object form elements (using the xtype property as the factory method.)
42753      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42754      * @param {Object} config 
42755      */
42756     addxtype : function(o)
42757     {
42758         // create the lement.
42759         o.form = this.form;
42760         var fe = Roo.factory(o, Roo.form);
42761         this.form.allItems.push(fe);
42762         this.stack.push(fe);
42763         
42764         if (fe.isFormField) {
42765             this.form.items.add(fe);
42766         }
42767          
42768         return fe;
42769     }
42770 });
42771
42772 /**
42773  * @class Roo.form.Column
42774  * @extends Roo.form.Layout
42775  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42776  * @constructor
42777  * @param {Object} config Configuration options
42778  */
42779 Roo.form.Column = function(config){
42780     Roo.form.Column.superclass.constructor.call(this, config);
42781 };
42782
42783 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42784     /**
42785      * @cfg {Number/String} width
42786      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42787      */
42788     /**
42789      * @cfg {String/Object} autoCreate
42790      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42791      */
42792
42793     // private
42794     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42795
42796     // private
42797     onRender : function(ct, position){
42798         Roo.form.Column.superclass.onRender.call(this, ct, position);
42799         if(this.width){
42800             this.el.setWidth(this.width);
42801         }
42802     }
42803 });
42804
42805
42806 /**
42807  * @class Roo.form.Row
42808  * @extends Roo.form.Layout
42809  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42810  * @constructor
42811  * @param {Object} config Configuration options
42812  */
42813
42814  
42815 Roo.form.Row = function(config){
42816     Roo.form.Row.superclass.constructor.call(this, config);
42817 };
42818  
42819 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42820       /**
42821      * @cfg {Number/String} width
42822      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42823      */
42824     /**
42825      * @cfg {Number/String} height
42826      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42827      */
42828     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42829     
42830     padWidth : 20,
42831     // private
42832     onRender : function(ct, position){
42833         //console.log('row render');
42834         if(!this.rowTpl){
42835             var t = new Roo.Template(
42836                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42837                     '<label for="{0}" style="{2}">{1}{4}</label>',
42838                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42839                     '</div>',
42840                 '</div>'
42841             );
42842             t.disableFormats = true;
42843             t.compile();
42844             Roo.form.Layout.prototype.rowTpl = t;
42845         }
42846         this.fieldTpl = this.rowTpl;
42847         
42848         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42849         var labelWidth = 100;
42850         
42851         if ((this.labelAlign != 'top')) {
42852             if (typeof this.labelWidth == 'number') {
42853                 labelWidth = this.labelWidth
42854             }
42855             this.padWidth =  20 + labelWidth;
42856             
42857         }
42858         
42859         Roo.form.Column.superclass.onRender.call(this, ct, position);
42860         if(this.width){
42861             this.el.setWidth(this.width);
42862         }
42863         if(this.height){
42864             this.el.setHeight(this.height);
42865         }
42866     },
42867     
42868     // private
42869     renderField : function(f){
42870         f.fieldEl = this.fieldTpl.append(this.el, [
42871                f.id, f.fieldLabel,
42872                f.labelStyle||this.labelStyle||'',
42873                this.elementStyle||'',
42874                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42875                f.itemCls||this.itemCls||'',
42876                f.width ? f.width + this.padWidth : 160 + this.padWidth
42877        ],true);
42878     }
42879 });
42880  
42881
42882 /**
42883  * @class Roo.form.FieldSet
42884  * @extends Roo.form.Layout
42885  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42886  * @constructor
42887  * @param {Object} config Configuration options
42888  */
42889 Roo.form.FieldSet = function(config){
42890     Roo.form.FieldSet.superclass.constructor.call(this, config);
42891 };
42892
42893 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42894     /**
42895      * @cfg {String} legend
42896      * The text to display as the legend for the FieldSet (defaults to '')
42897      */
42898     /**
42899      * @cfg {String/Object} autoCreate
42900      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42901      */
42902
42903     // private
42904     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42905
42906     // private
42907     onRender : function(ct, position){
42908         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42909         if(this.legend){
42910             this.setLegend(this.legend);
42911         }
42912     },
42913
42914     // private
42915     setLegend : function(text){
42916         if(this.rendered){
42917             this.el.child('legend').update(text);
42918         }
42919     }
42920 });/*
42921  * Based on:
42922  * Ext JS Library 1.1.1
42923  * Copyright(c) 2006-2007, Ext JS, LLC.
42924  *
42925  * Originally Released Under LGPL - original licence link has changed is not relivant.
42926  *
42927  * Fork - LGPL
42928  * <script type="text/javascript">
42929  */
42930 /**
42931  * @class Roo.form.VTypes
42932  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42933  * @singleton
42934  */
42935 Roo.form.VTypes = function(){
42936     // closure these in so they are only created once.
42937     var alpha = /^[a-zA-Z_]+$/;
42938     var alphanum = /^[a-zA-Z0-9_]+$/;
42939     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42940     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42941
42942     // All these messages and functions are configurable
42943     return {
42944         /**
42945          * The function used to validate email addresses
42946          * @param {String} value The email address
42947          */
42948         'email' : function(v){
42949             return email.test(v);
42950         },
42951         /**
42952          * The error text to display when the email validation function returns false
42953          * @type String
42954          */
42955         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42956         /**
42957          * The keystroke filter mask to be applied on email input
42958          * @type RegExp
42959          */
42960         'emailMask' : /[a-z0-9_\.\-@]/i,
42961
42962         /**
42963          * The function used to validate URLs
42964          * @param {String} value The URL
42965          */
42966         'url' : function(v){
42967             return url.test(v);
42968         },
42969         /**
42970          * The error text to display when the url validation function returns false
42971          * @type String
42972          */
42973         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42974         
42975         /**
42976          * The function used to validate alpha values
42977          * @param {String} value The value
42978          */
42979         'alpha' : function(v){
42980             return alpha.test(v);
42981         },
42982         /**
42983          * The error text to display when the alpha validation function returns false
42984          * @type String
42985          */
42986         'alphaText' : 'This field should only contain letters and _',
42987         /**
42988          * The keystroke filter mask to be applied on alpha input
42989          * @type RegExp
42990          */
42991         'alphaMask' : /[a-z_]/i,
42992
42993         /**
42994          * The function used to validate alphanumeric values
42995          * @param {String} value The value
42996          */
42997         'alphanum' : function(v){
42998             return alphanum.test(v);
42999         },
43000         /**
43001          * The error text to display when the alphanumeric validation function returns false
43002          * @type String
43003          */
43004         'alphanumText' : 'This field should only contain letters, numbers and _',
43005         /**
43006          * The keystroke filter mask to be applied on alphanumeric input
43007          * @type RegExp
43008          */
43009         'alphanumMask' : /[a-z0-9_]/i
43010     };
43011 }();//<script type="text/javascript">
43012
43013 /**
43014  * @class Roo.form.FCKeditor
43015  * @extends Roo.form.TextArea
43016  * Wrapper around the FCKEditor http://www.fckeditor.net
43017  * @constructor
43018  * Creates a new FCKeditor
43019  * @param {Object} config Configuration options
43020  */
43021 Roo.form.FCKeditor = function(config){
43022     Roo.form.FCKeditor.superclass.constructor.call(this, config);
43023     this.addEvents({
43024          /**
43025          * @event editorinit
43026          * Fired when the editor is initialized - you can add extra handlers here..
43027          * @param {FCKeditor} this
43028          * @param {Object} the FCK object.
43029          */
43030         editorinit : true
43031     });
43032     
43033     
43034 };
43035 Roo.form.FCKeditor.editors = { };
43036 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
43037 {
43038     //defaultAutoCreate : {
43039     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
43040     //},
43041     // private
43042     /**
43043      * @cfg {Object} fck options - see fck manual for details.
43044      */
43045     fckconfig : false,
43046     
43047     /**
43048      * @cfg {Object} fck toolbar set (Basic or Default)
43049      */
43050     toolbarSet : 'Basic',
43051     /**
43052      * @cfg {Object} fck BasePath
43053      */ 
43054     basePath : '/fckeditor/',
43055     
43056     
43057     frame : false,
43058     
43059     value : '',
43060     
43061    
43062     onRender : function(ct, position)
43063     {
43064         if(!this.el){
43065             this.defaultAutoCreate = {
43066                 tag: "textarea",
43067                 style:"width:300px;height:60px;",
43068                 autocomplete: "off"
43069             };
43070         }
43071         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
43072         /*
43073         if(this.grow){
43074             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
43075             if(this.preventScrollbars){
43076                 this.el.setStyle("overflow", "hidden");
43077             }
43078             this.el.setHeight(this.growMin);
43079         }
43080         */
43081         //console.log('onrender' + this.getId() );
43082         Roo.form.FCKeditor.editors[this.getId()] = this;
43083          
43084
43085         this.replaceTextarea() ;
43086         
43087     },
43088     
43089     getEditor : function() {
43090         return this.fckEditor;
43091     },
43092     /**
43093      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
43094      * @param {Mixed} value The value to set
43095      */
43096     
43097     
43098     setValue : function(value)
43099     {
43100         //console.log('setValue: ' + value);
43101         
43102         if(typeof(value) == 'undefined') { // not sure why this is happending...
43103             return;
43104         }
43105         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43106         
43107         //if(!this.el || !this.getEditor()) {
43108         //    this.value = value;
43109             //this.setValue.defer(100,this,[value]);    
43110         //    return;
43111         //} 
43112         
43113         if(!this.getEditor()) {
43114             return;
43115         }
43116         
43117         this.getEditor().SetData(value);
43118         
43119         //
43120
43121     },
43122
43123     /**
43124      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
43125      * @return {Mixed} value The field value
43126      */
43127     getValue : function()
43128     {
43129         
43130         if (this.frame && this.frame.dom.style.display == 'none') {
43131             return Roo.form.FCKeditor.superclass.getValue.call(this);
43132         }
43133         
43134         if(!this.el || !this.getEditor()) {
43135            
43136            // this.getValue.defer(100,this); 
43137             return this.value;
43138         }
43139        
43140         
43141         var value=this.getEditor().GetData();
43142         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43143         return Roo.form.FCKeditor.superclass.getValue.call(this);
43144         
43145
43146     },
43147
43148     /**
43149      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
43150      * @return {Mixed} value The field value
43151      */
43152     getRawValue : function()
43153     {
43154         if (this.frame && this.frame.dom.style.display == 'none') {
43155             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43156         }
43157         
43158         if(!this.el || !this.getEditor()) {
43159             //this.getRawValue.defer(100,this); 
43160             return this.value;
43161             return;
43162         }
43163         
43164         
43165         
43166         var value=this.getEditor().GetData();
43167         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43168         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43169          
43170     },
43171     
43172     setSize : function(w,h) {
43173         
43174         
43175         
43176         //if (this.frame && this.frame.dom.style.display == 'none') {
43177         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43178         //    return;
43179         //}
43180         //if(!this.el || !this.getEditor()) {
43181         //    this.setSize.defer(100,this, [w,h]); 
43182         //    return;
43183         //}
43184         
43185         
43186         
43187         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43188         
43189         this.frame.dom.setAttribute('width', w);
43190         this.frame.dom.setAttribute('height', h);
43191         this.frame.setSize(w,h);
43192         
43193     },
43194     
43195     toggleSourceEdit : function(value) {
43196         
43197       
43198          
43199         this.el.dom.style.display = value ? '' : 'none';
43200         this.frame.dom.style.display = value ?  'none' : '';
43201         
43202     },
43203     
43204     
43205     focus: function(tag)
43206     {
43207         if (this.frame.dom.style.display == 'none') {
43208             return Roo.form.FCKeditor.superclass.focus.call(this);
43209         }
43210         if(!this.el || !this.getEditor()) {
43211             this.focus.defer(100,this, [tag]); 
43212             return;
43213         }
43214         
43215         
43216         
43217         
43218         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43219         this.getEditor().Focus();
43220         if (tgs.length) {
43221             if (!this.getEditor().Selection.GetSelection()) {
43222                 this.focus.defer(100,this, [tag]); 
43223                 return;
43224             }
43225             
43226             
43227             var r = this.getEditor().EditorDocument.createRange();
43228             r.setStart(tgs[0],0);
43229             r.setEnd(tgs[0],0);
43230             this.getEditor().Selection.GetSelection().removeAllRanges();
43231             this.getEditor().Selection.GetSelection().addRange(r);
43232             this.getEditor().Focus();
43233         }
43234         
43235     },
43236     
43237     
43238     
43239     replaceTextarea : function()
43240     {
43241         if ( document.getElementById( this.getId() + '___Frame' ) )
43242             return ;
43243         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43244         //{
43245             // We must check the elements firstly using the Id and then the name.
43246         var oTextarea = document.getElementById( this.getId() );
43247         
43248         var colElementsByName = document.getElementsByName( this.getId() ) ;
43249          
43250         oTextarea.style.display = 'none' ;
43251
43252         if ( oTextarea.tabIndex ) {            
43253             this.TabIndex = oTextarea.tabIndex ;
43254         }
43255         
43256         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43257         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43258         this.frame = Roo.get(this.getId() + '___Frame')
43259     },
43260     
43261     _getConfigHtml : function()
43262     {
43263         var sConfig = '' ;
43264
43265         for ( var o in this.fckconfig ) {
43266             sConfig += sConfig.length > 0  ? '&amp;' : '';
43267             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43268         }
43269
43270         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43271     },
43272     
43273     
43274     _getIFrameHtml : function()
43275     {
43276         var sFile = 'fckeditor.html' ;
43277         /* no idea what this is about..
43278         try
43279         {
43280             if ( (/fcksource=true/i).test( window.top.location.search ) )
43281                 sFile = 'fckeditor.original.html' ;
43282         }
43283         catch (e) { 
43284         */
43285
43286         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43287         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43288         
43289         
43290         var html = '<iframe id="' + this.getId() +
43291             '___Frame" src="' + sLink +
43292             '" width="' + this.width +
43293             '" height="' + this.height + '"' +
43294             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43295             ' frameborder="0" scrolling="no"></iframe>' ;
43296
43297         return html ;
43298     },
43299     
43300     _insertHtmlBefore : function( html, element )
43301     {
43302         if ( element.insertAdjacentHTML )       {
43303             // IE
43304             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43305         } else { // Gecko
43306             var oRange = document.createRange() ;
43307             oRange.setStartBefore( element ) ;
43308             var oFragment = oRange.createContextualFragment( html );
43309             element.parentNode.insertBefore( oFragment, element ) ;
43310         }
43311     }
43312     
43313     
43314   
43315     
43316     
43317     
43318     
43319
43320 });
43321
43322 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43323
43324 function FCKeditor_OnComplete(editorInstance){
43325     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43326     f.fckEditor = editorInstance;
43327     //console.log("loaded");
43328     f.fireEvent('editorinit', f, editorInstance);
43329
43330   
43331
43332  
43333
43334
43335
43336
43337
43338
43339
43340
43341
43342
43343
43344
43345
43346
43347
43348 //<script type="text/javascript">
43349 /**
43350  * @class Roo.form.GridField
43351  * @extends Roo.form.Field
43352  * Embed a grid (or editable grid into a form)
43353  * STATUS ALPHA
43354  * 
43355  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43356  * it needs 
43357  * xgrid.store = Roo.data.Store
43358  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43359  * xgrid.store.reader = Roo.data.JsonReader 
43360  * 
43361  * 
43362  * @constructor
43363  * Creates a new GridField
43364  * @param {Object} config Configuration options
43365  */
43366 Roo.form.GridField = function(config){
43367     Roo.form.GridField.superclass.constructor.call(this, config);
43368      
43369 };
43370
43371 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43372     /**
43373      * @cfg {Number} width  - used to restrict width of grid..
43374      */
43375     width : 100,
43376     /**
43377      * @cfg {Number} height - used to restrict height of grid..
43378      */
43379     height : 50,
43380      /**
43381      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43382          * 
43383          *}
43384      */
43385     xgrid : false, 
43386     /**
43387      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43388      * {tag: "input", type: "checkbox", autocomplete: "off"})
43389      */
43390    // defaultAutoCreate : { tag: 'div' },
43391     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43392     /**
43393      * @cfg {String} addTitle Text to include for adding a title.
43394      */
43395     addTitle : false,
43396     //
43397     onResize : function(){
43398         Roo.form.Field.superclass.onResize.apply(this, arguments);
43399     },
43400
43401     initEvents : function(){
43402         // Roo.form.Checkbox.superclass.initEvents.call(this);
43403         // has no events...
43404        
43405     },
43406
43407
43408     getResizeEl : function(){
43409         return this.wrap;
43410     },
43411
43412     getPositionEl : function(){
43413         return this.wrap;
43414     },
43415
43416     // private
43417     onRender : function(ct, position){
43418         
43419         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43420         var style = this.style;
43421         delete this.style;
43422         
43423         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43424         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43425         this.viewEl = this.wrap.createChild({ tag: 'div' });
43426         if (style) {
43427             this.viewEl.applyStyles(style);
43428         }
43429         if (this.width) {
43430             this.viewEl.setWidth(this.width);
43431         }
43432         if (this.height) {
43433             this.viewEl.setHeight(this.height);
43434         }
43435         //if(this.inputValue !== undefined){
43436         //this.setValue(this.value);
43437         
43438         
43439         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43440         
43441         
43442         this.grid.render();
43443         this.grid.getDataSource().on('remove', this.refreshValue, this);
43444         this.grid.getDataSource().on('update', this.refreshValue, this);
43445         this.grid.on('afteredit', this.refreshValue, this);
43446  
43447     },
43448      
43449     
43450     /**
43451      * Sets the value of the item. 
43452      * @param {String} either an object  or a string..
43453      */
43454     setValue : function(v){
43455         //this.value = v;
43456         v = v || []; // empty set..
43457         // this does not seem smart - it really only affects memoryproxy grids..
43458         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43459             var ds = this.grid.getDataSource();
43460             // assumes a json reader..
43461             var data = {}
43462             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43463             ds.loadData( data);
43464         }
43465         // clear selection so it does not get stale.
43466         if (this.grid.sm) { 
43467             this.grid.sm.clearSelections();
43468         }
43469         
43470         Roo.form.GridField.superclass.setValue.call(this, v);
43471         this.refreshValue();
43472         // should load data in the grid really....
43473     },
43474     
43475     // private
43476     refreshValue: function() {
43477          var val = [];
43478         this.grid.getDataSource().each(function(r) {
43479             val.push(r.data);
43480         });
43481         this.el.dom.value = Roo.encode(val);
43482     }
43483     
43484      
43485     
43486     
43487 });/*
43488  * Based on:
43489  * Ext JS Library 1.1.1
43490  * Copyright(c) 2006-2007, Ext JS, LLC.
43491  *
43492  * Originally Released Under LGPL - original licence link has changed is not relivant.
43493  *
43494  * Fork - LGPL
43495  * <script type="text/javascript">
43496  */
43497 /**
43498  * @class Roo.form.DisplayField
43499  * @extends Roo.form.Field
43500  * A generic Field to display non-editable data.
43501  * @constructor
43502  * Creates a new Display Field item.
43503  * @param {Object} config Configuration options
43504  */
43505 Roo.form.DisplayField = function(config){
43506     Roo.form.DisplayField.superclass.constructor.call(this, config);
43507     
43508 };
43509
43510 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43511     inputType:      'hidden',
43512     allowBlank:     true,
43513     readOnly:         true,
43514     
43515  
43516     /**
43517      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43518      */
43519     focusClass : undefined,
43520     /**
43521      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43522      */
43523     fieldClass: 'x-form-field',
43524     
43525      /**
43526      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43527      */
43528     valueRenderer: undefined,
43529     
43530     width: 100,
43531     /**
43532      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43533      * {tag: "input", type: "checkbox", autocomplete: "off"})
43534      */
43535      
43536  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43537
43538     onResize : function(){
43539         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43540         
43541     },
43542
43543     initEvents : function(){
43544         // Roo.form.Checkbox.superclass.initEvents.call(this);
43545         // has no events...
43546        
43547     },
43548
43549
43550     getResizeEl : function(){
43551         return this.wrap;
43552     },
43553
43554     getPositionEl : function(){
43555         return this.wrap;
43556     },
43557
43558     // private
43559     onRender : function(ct, position){
43560         
43561         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43562         //if(this.inputValue !== undefined){
43563         this.wrap = this.el.wrap();
43564         
43565         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43566         
43567         if (this.bodyStyle) {
43568             this.viewEl.applyStyles(this.bodyStyle);
43569         }
43570         //this.viewEl.setStyle('padding', '2px');
43571         
43572         this.setValue(this.value);
43573         
43574     },
43575 /*
43576     // private
43577     initValue : Roo.emptyFn,
43578
43579   */
43580
43581         // private
43582     onClick : function(){
43583         
43584     },
43585
43586     /**
43587      * Sets the checked state of the checkbox.
43588      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43589      */
43590     setValue : function(v){
43591         this.value = v;
43592         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43593         // this might be called before we have a dom element..
43594         if (!this.viewEl) {
43595             return;
43596         }
43597         this.viewEl.dom.innerHTML = html;
43598         Roo.form.DisplayField.superclass.setValue.call(this, v);
43599
43600     }
43601 });/*
43602  * 
43603  * Licence- LGPL
43604  * 
43605  */
43606
43607 /**
43608  * @class Roo.form.DayPicker
43609  * @extends Roo.form.Field
43610  * A Day picker show [M] [T] [W] ....
43611  * @constructor
43612  * Creates a new Day Picker
43613  * @param {Object} config Configuration options
43614  */
43615 Roo.form.DayPicker= function(config){
43616     Roo.form.DayPicker.superclass.constructor.call(this, config);
43617      
43618 };
43619
43620 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43621     /**
43622      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43623      */
43624     focusClass : undefined,
43625     /**
43626      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43627      */
43628     fieldClass: "x-form-field",
43629    
43630     /**
43631      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43632      * {tag: "input", type: "checkbox", autocomplete: "off"})
43633      */
43634     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43635     
43636    
43637     actionMode : 'viewEl', 
43638     //
43639     // private
43640  
43641     inputType : 'hidden',
43642     
43643      
43644     inputElement: false, // real input element?
43645     basedOn: false, // ????
43646     
43647     isFormField: true, // not sure where this is needed!!!!
43648
43649     onResize : function(){
43650         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43651         if(!this.boxLabel){
43652             this.el.alignTo(this.wrap, 'c-c');
43653         }
43654     },
43655
43656     initEvents : function(){
43657         Roo.form.Checkbox.superclass.initEvents.call(this);
43658         this.el.on("click", this.onClick,  this);
43659         this.el.on("change", this.onClick,  this);
43660     },
43661
43662
43663     getResizeEl : function(){
43664         return this.wrap;
43665     },
43666
43667     getPositionEl : function(){
43668         return this.wrap;
43669     },
43670
43671     
43672     // private
43673     onRender : function(ct, position){
43674         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43675        
43676         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43677         
43678         var r1 = '<table><tr>';
43679         var r2 = '<tr class="x-form-daypick-icons">';
43680         for (var i=0; i < 7; i++) {
43681             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43682             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43683         }
43684         
43685         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43686         viewEl.select('img').on('click', this.onClick, this);
43687         this.viewEl = viewEl;   
43688         
43689         
43690         // this will not work on Chrome!!!
43691         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43692         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43693         
43694         
43695           
43696
43697     },
43698
43699     // private
43700     initValue : Roo.emptyFn,
43701
43702     /**
43703      * Returns the checked state of the checkbox.
43704      * @return {Boolean} True if checked, else false
43705      */
43706     getValue : function(){
43707         return this.el.dom.value;
43708         
43709     },
43710
43711         // private
43712     onClick : function(e){ 
43713         //this.setChecked(!this.checked);
43714         Roo.get(e.target).toggleClass('x-menu-item-checked');
43715         this.refreshValue();
43716         //if(this.el.dom.checked != this.checked){
43717         //    this.setValue(this.el.dom.checked);
43718        // }
43719     },
43720     
43721     // private
43722     refreshValue : function()
43723     {
43724         var val = '';
43725         this.viewEl.select('img',true).each(function(e,i,n)  {
43726             val += e.is(".x-menu-item-checked") ? String(n) : '';
43727         });
43728         this.setValue(val, true);
43729     },
43730
43731     /**
43732      * Sets the checked state of the checkbox.
43733      * On is always based on a string comparison between inputValue and the param.
43734      * @param {Boolean/String} value - the value to set 
43735      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43736      */
43737     setValue : function(v,suppressEvent){
43738         if (!this.el.dom) {
43739             return;
43740         }
43741         var old = this.el.dom.value ;
43742         this.el.dom.value = v;
43743         if (suppressEvent) {
43744             return ;
43745         }
43746          
43747         // update display..
43748         this.viewEl.select('img',true).each(function(e,i,n)  {
43749             
43750             var on = e.is(".x-menu-item-checked");
43751             var newv = v.indexOf(String(n)) > -1;
43752             if (on != newv) {
43753                 e.toggleClass('x-menu-item-checked');
43754             }
43755             
43756         });
43757         
43758         
43759         this.fireEvent('change', this, v, old);
43760         
43761         
43762     },
43763    
43764     // handle setting of hidden value by some other method!!?!?
43765     setFromHidden: function()
43766     {
43767         if(!this.el){
43768             return;
43769         }
43770         //console.log("SET FROM HIDDEN");
43771         //alert('setFrom hidden');
43772         this.setValue(this.el.dom.value);
43773     },
43774     
43775     onDestroy : function()
43776     {
43777         if(this.viewEl){
43778             Roo.get(this.viewEl).remove();
43779         }
43780          
43781         Roo.form.DayPicker.superclass.onDestroy.call(this);
43782     }
43783
43784 });/*
43785  * RooJS Library 1.1.1
43786  * Copyright(c) 2008-2011  Alan Knowles
43787  *
43788  * License - LGPL
43789  */
43790  
43791
43792 /**
43793  * @class Roo.form.ComboCheck
43794  * @extends Roo.form.ComboBox
43795  * A combobox for multiple select items.
43796  *
43797  * FIXME - could do with a reset button..
43798  * 
43799  * @constructor
43800  * Create a new ComboCheck
43801  * @param {Object} config Configuration options
43802  */
43803 Roo.form.ComboCheck = function(config){
43804     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43805     // should verify some data...
43806     // like
43807     // hiddenName = required..
43808     // displayField = required
43809     // valudField == required
43810     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43811     var _t = this;
43812     Roo.each(req, function(e) {
43813         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43814             throw "Roo.form.ComboCheck : missing value for: " + e;
43815         }
43816     });
43817     
43818     
43819 };
43820
43821 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43822      
43823      
43824     editable : false,
43825      
43826     selectedClass: 'x-menu-item-checked', 
43827     
43828     // private
43829     onRender : function(ct, position){
43830         var _t = this;
43831         
43832         
43833         
43834         if(!this.tpl){
43835             var cls = 'x-combo-list';
43836
43837             
43838             this.tpl =  new Roo.Template({
43839                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43840                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43841                    '<span>{' + this.displayField + '}</span>' +
43842                     '</div>' 
43843                 
43844             });
43845         }
43846  
43847         
43848         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43849         this.view.singleSelect = false;
43850         this.view.multiSelect = true;
43851         this.view.toggleSelect = true;
43852         this.pageTb.add(new Roo.Toolbar.Fill(), {
43853             
43854             text: 'Done',
43855             handler: function()
43856             {
43857                 _t.collapse();
43858             }
43859         });
43860     },
43861     
43862     onViewOver : function(e, t){
43863         // do nothing...
43864         return;
43865         
43866     },
43867     
43868     onViewClick : function(doFocus,index){
43869         return;
43870         
43871     },
43872     select: function () {
43873         //Roo.log("SELECT CALLED");
43874     },
43875      
43876     selectByValue : function(xv, scrollIntoView){
43877         var ar = this.getValueArray();
43878         var sels = [];
43879         
43880         Roo.each(ar, function(v) {
43881             if(v === undefined || v === null){
43882                 return;
43883             }
43884             var r = this.findRecord(this.valueField, v);
43885             if(r){
43886                 sels.push(this.store.indexOf(r))
43887                 
43888             }
43889         },this);
43890         this.view.select(sels);
43891         return false;
43892     },
43893     
43894     
43895     
43896     onSelect : function(record, index){
43897        // Roo.log("onselect Called");
43898        // this is only called by the clear button now..
43899         this.view.clearSelections();
43900         this.setValue('[]');
43901         if (this.value != this.valueBefore) {
43902             this.fireEvent('change', this, this.value, this.valueBefore);
43903         }
43904     },
43905     getValueArray : function()
43906     {
43907         var ar = [] ;
43908         
43909         try {
43910             //Roo.log(this.value);
43911             if (typeof(this.value) == 'undefined') {
43912                 return [];
43913             }
43914             var ar = Roo.decode(this.value);
43915             return  ar instanceof Array ? ar : []; //?? valid?
43916             
43917         } catch(e) {
43918             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43919             return [];
43920         }
43921          
43922     },
43923     expand : function ()
43924     {
43925         Roo.form.ComboCheck.superclass.expand.call(this);
43926         this.valueBefore = this.value;
43927         
43928
43929     },
43930     
43931     collapse : function(){
43932         Roo.form.ComboCheck.superclass.collapse.call(this);
43933         var sl = this.view.getSelectedIndexes();
43934         var st = this.store;
43935         var nv = [];
43936         var tv = [];
43937         var r;
43938         Roo.each(sl, function(i) {
43939             r = st.getAt(i);
43940             nv.push(r.get(this.valueField));
43941         },this);
43942         this.setValue(Roo.encode(nv));
43943         if (this.value != this.valueBefore) {
43944
43945             this.fireEvent('change', this, this.value, this.valueBefore);
43946         }
43947         
43948     },
43949     
43950     setValue : function(v){
43951         // Roo.log(v);
43952         this.value = v;
43953         
43954         var vals = this.getValueArray();
43955         var tv = [];
43956         Roo.each(vals, function(k) {
43957             var r = this.findRecord(this.valueField, k);
43958             if(r){
43959                 tv.push(r.data[this.displayField]);
43960             }else if(this.valueNotFoundText !== undefined){
43961                 tv.push( this.valueNotFoundText );
43962             }
43963         },this);
43964        // Roo.log(tv);
43965         
43966         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43967         this.hiddenField.value = v;
43968         this.value = v;
43969     }
43970     
43971 });//<script type="text/javasscript">
43972  
43973
43974 /**
43975  * @class Roo.DDView
43976  * A DnD enabled version of Roo.View.
43977  * @param {Element/String} container The Element in which to create the View.
43978  * @param {String} tpl The template string used to create the markup for each element of the View
43979  * @param {Object} config The configuration properties. These include all the config options of
43980  * {@link Roo.View} plus some specific to this class.<br>
43981  * <p>
43982  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43983  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43984  * <p>
43985  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43986 .x-view-drag-insert-above {
43987         border-top:1px dotted #3366cc;
43988 }
43989 .x-view-drag-insert-below {
43990         border-bottom:1px dotted #3366cc;
43991 }
43992 </code></pre>
43993  * 
43994  */
43995  
43996 Roo.DDView = function(container, tpl, config) {
43997     Roo.DDView.superclass.constructor.apply(this, arguments);
43998     this.getEl().setStyle("outline", "0px none");
43999     this.getEl().unselectable();
44000     if (this.dragGroup) {
44001                 this.setDraggable(this.dragGroup.split(","));
44002     }
44003     if (this.dropGroup) {
44004                 this.setDroppable(this.dropGroup.split(","));
44005     }
44006     if (this.deletable) {
44007         this.setDeletable();
44008     }
44009     this.isDirtyFlag = false;
44010         this.addEvents({
44011                 "drop" : true
44012         });
44013 };
44014
44015 Roo.extend(Roo.DDView, Roo.View, {
44016 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
44017 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
44018 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
44019 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
44020
44021         isFormField: true,
44022
44023         reset: Roo.emptyFn,
44024         
44025         clearInvalid: Roo.form.Field.prototype.clearInvalid,
44026
44027         validate: function() {
44028                 return true;
44029         },
44030         
44031         destroy: function() {
44032                 this.purgeListeners();
44033                 this.getEl.removeAllListeners();
44034                 this.getEl().remove();
44035                 if (this.dragZone) {
44036                         if (this.dragZone.destroy) {
44037                                 this.dragZone.destroy();
44038                         }
44039                 }
44040                 if (this.dropZone) {
44041                         if (this.dropZone.destroy) {
44042                                 this.dropZone.destroy();
44043                         }
44044                 }
44045         },
44046
44047 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
44048         getName: function() {
44049                 return this.name;
44050         },
44051
44052 /**     Loads the View from a JSON string representing the Records to put into the Store. */
44053         setValue: function(v) {
44054                 if (!this.store) {
44055                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
44056                 }
44057                 var data = {};
44058                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
44059                 this.store.proxy = new Roo.data.MemoryProxy(data);
44060                 this.store.load();
44061         },
44062
44063 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
44064         getValue: function() {
44065                 var result = '(';
44066                 this.store.each(function(rec) {
44067                         result += rec.id + ',';
44068                 });
44069                 return result.substr(0, result.length - 1) + ')';
44070         },
44071         
44072         getIds: function() {
44073                 var i = 0, result = new Array(this.store.getCount());
44074                 this.store.each(function(rec) {
44075                         result[i++] = rec.id;
44076                 });
44077                 return result;
44078         },
44079         
44080         isDirty: function() {
44081                 return this.isDirtyFlag;
44082         },
44083
44084 /**
44085  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
44086  *      whole Element becomes the target, and this causes the drop gesture to append.
44087  */
44088     getTargetFromEvent : function(e) {
44089                 var target = e.getTarget();
44090                 while ((target !== null) && (target.parentNode != this.el.dom)) {
44091                 target = target.parentNode;
44092                 }
44093                 if (!target) {
44094                         target = this.el.dom.lastChild || this.el.dom;
44095                 }
44096                 return target;
44097     },
44098
44099 /**
44100  *      Create the drag data which consists of an object which has the property "ddel" as
44101  *      the drag proxy element. 
44102  */
44103     getDragData : function(e) {
44104         var target = this.findItemFromChild(e.getTarget());
44105                 if(target) {
44106                         this.handleSelection(e);
44107                         var selNodes = this.getSelectedNodes();
44108             var dragData = {
44109                 source: this,
44110                 copy: this.copy || (this.allowCopy && e.ctrlKey),
44111                 nodes: selNodes,
44112                 records: []
44113                         };
44114                         var selectedIndices = this.getSelectedIndexes();
44115                         for (var i = 0; i < selectedIndices.length; i++) {
44116                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
44117                         }
44118                         if (selNodes.length == 1) {
44119                                 dragData.ddel = target.cloneNode(true); // the div element
44120                         } else {
44121                                 var div = document.createElement('div'); // create the multi element drag "ghost"
44122                                 div.className = 'multi-proxy';
44123                                 for (var i = 0, len = selNodes.length; i < len; i++) {
44124                                         div.appendChild(selNodes[i].cloneNode(true));
44125                                 }
44126                                 dragData.ddel = div;
44127                         }
44128             //console.log(dragData)
44129             //console.log(dragData.ddel.innerHTML)
44130                         return dragData;
44131                 }
44132         //console.log('nodragData')
44133                 return false;
44134     },
44135     
44136 /**     Specify to which ddGroup items in this DDView may be dragged. */
44137     setDraggable: function(ddGroup) {
44138         if (ddGroup instanceof Array) {
44139                 Roo.each(ddGroup, this.setDraggable, this);
44140                 return;
44141         }
44142         if (this.dragZone) {
44143                 this.dragZone.addToGroup(ddGroup);
44144         } else {
44145                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
44146                                 containerScroll: true,
44147                                 ddGroup: ddGroup 
44148
44149                         });
44150 //                      Draggability implies selection. DragZone's mousedown selects the element.
44151                         if (!this.multiSelect) { this.singleSelect = true; }
44152
44153 //                      Wire the DragZone's handlers up to methods in *this*
44154                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
44155                 }
44156     },
44157
44158 /**     Specify from which ddGroup this DDView accepts drops. */
44159     setDroppable: function(ddGroup) {
44160         if (ddGroup instanceof Array) {
44161                 Roo.each(ddGroup, this.setDroppable, this);
44162                 return;
44163         }
44164         if (this.dropZone) {
44165                 this.dropZone.addToGroup(ddGroup);
44166         } else {
44167                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44168                                 containerScroll: true,
44169                                 ddGroup: ddGroup
44170                         });
44171
44172 //                      Wire the DropZone's handlers up to methods in *this*
44173                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44174                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44175                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44176                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44177                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44178                 }
44179     },
44180
44181 /**     Decide whether to drop above or below a View node. */
44182     getDropPoint : function(e, n, dd){
44183         if (n == this.el.dom) { return "above"; }
44184                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44185                 var c = t + (b - t) / 2;
44186                 var y = Roo.lib.Event.getPageY(e);
44187                 if(y <= c) {
44188                         return "above";
44189                 }else{
44190                         return "below";
44191                 }
44192     },
44193
44194     onNodeEnter : function(n, dd, e, data){
44195                 return false;
44196     },
44197     
44198     onNodeOver : function(n, dd, e, data){
44199                 var pt = this.getDropPoint(e, n, dd);
44200                 // set the insert point style on the target node
44201                 var dragElClass = this.dropNotAllowed;
44202                 if (pt) {
44203                         var targetElClass;
44204                         if (pt == "above"){
44205                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44206                                 targetElClass = "x-view-drag-insert-above";
44207                         } else {
44208                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44209                                 targetElClass = "x-view-drag-insert-below";
44210                         }
44211                         if (this.lastInsertClass != targetElClass){
44212                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44213                                 this.lastInsertClass = targetElClass;
44214                         }
44215                 }
44216                 return dragElClass;
44217         },
44218
44219     onNodeOut : function(n, dd, e, data){
44220                 this.removeDropIndicators(n);
44221     },
44222
44223     onNodeDrop : function(n, dd, e, data){
44224         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44225                 return false;
44226         }
44227         var pt = this.getDropPoint(e, n, dd);
44228                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44229                 if (pt == "below") { insertAt++; }
44230                 for (var i = 0; i < data.records.length; i++) {
44231                         var r = data.records[i];
44232                         var dup = this.store.getById(r.id);
44233                         if (dup && (dd != this.dragZone)) {
44234                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44235                         } else {
44236                                 if (data.copy) {
44237                                         this.store.insert(insertAt++, r.copy());
44238                                 } else {
44239                                         data.source.isDirtyFlag = true;
44240                                         r.store.remove(r);
44241                                         this.store.insert(insertAt++, r);
44242                                 }
44243                                 this.isDirtyFlag = true;
44244                         }
44245                 }
44246                 this.dragZone.cachedTarget = null;
44247                 return true;
44248     },
44249
44250     removeDropIndicators : function(n){
44251                 if(n){
44252                         Roo.fly(n).removeClass([
44253                                 "x-view-drag-insert-above",
44254                                 "x-view-drag-insert-below"]);
44255                         this.lastInsertClass = "_noclass";
44256                 }
44257     },
44258
44259 /**
44260  *      Utility method. Add a delete option to the DDView's context menu.
44261  *      @param {String} imageUrl The URL of the "delete" icon image.
44262  */
44263         setDeletable: function(imageUrl) {
44264                 if (!this.singleSelect && !this.multiSelect) {
44265                         this.singleSelect = true;
44266                 }
44267                 var c = this.getContextMenu();
44268                 this.contextMenu.on("itemclick", function(item) {
44269                         switch (item.id) {
44270                                 case "delete":
44271                                         this.remove(this.getSelectedIndexes());
44272                                         break;
44273                         }
44274                 }, this);
44275                 this.contextMenu.add({
44276                         icon: imageUrl,
44277                         id: "delete",
44278                         text: 'Delete'
44279                 });
44280         },
44281         
44282 /**     Return the context menu for this DDView. */
44283         getContextMenu: function() {
44284                 if (!this.contextMenu) {
44285 //                      Create the View's context menu
44286                         this.contextMenu = new Roo.menu.Menu({
44287                                 id: this.id + "-contextmenu"
44288                         });
44289                         this.el.on("contextmenu", this.showContextMenu, this);
44290                 }
44291                 return this.contextMenu;
44292         },
44293         
44294         disableContextMenu: function() {
44295                 if (this.contextMenu) {
44296                         this.el.un("contextmenu", this.showContextMenu, this);
44297                 }
44298         },
44299
44300         showContextMenu: function(e, item) {
44301         item = this.findItemFromChild(e.getTarget());
44302                 if (item) {
44303                         e.stopEvent();
44304                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44305                         this.contextMenu.showAt(e.getXY());
44306             }
44307     },
44308
44309 /**
44310  *      Remove {@link Roo.data.Record}s at the specified indices.
44311  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44312  */
44313     remove: function(selectedIndices) {
44314                 selectedIndices = [].concat(selectedIndices);
44315                 for (var i = 0; i < selectedIndices.length; i++) {
44316                         var rec = this.store.getAt(selectedIndices[i]);
44317                         this.store.remove(rec);
44318                 }
44319     },
44320
44321 /**
44322  *      Double click fires the event, but also, if this is draggable, and there is only one other
44323  *      related DropZone, it transfers the selected node.
44324  */
44325     onDblClick : function(e){
44326         var item = this.findItemFromChild(e.getTarget());
44327         if(item){
44328             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44329                 return false;
44330             }
44331             if (this.dragGroup) {
44332                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44333                     while (targets.indexOf(this.dropZone) > -1) {
44334                             targets.remove(this.dropZone);
44335                                 }
44336                     if (targets.length == 1) {
44337                                         this.dragZone.cachedTarget = null;
44338                         var el = Roo.get(targets[0].getEl());
44339                         var box = el.getBox(true);
44340                         targets[0].onNodeDrop(el.dom, {
44341                                 target: el.dom,
44342                                 xy: [box.x, box.y + box.height - 1]
44343                         }, null, this.getDragData(e));
44344                     }
44345                 }
44346         }
44347     },
44348     
44349     handleSelection: function(e) {
44350                 this.dragZone.cachedTarget = null;
44351         var item = this.findItemFromChild(e.getTarget());
44352         if (!item) {
44353                 this.clearSelections(true);
44354                 return;
44355         }
44356                 if (item && (this.multiSelect || this.singleSelect)){
44357                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44358                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44359                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44360                                 this.unselect(item);
44361                         } else {
44362                                 this.select(item, this.multiSelect && e.ctrlKey);
44363                                 this.lastSelection = item;
44364                         }
44365                 }
44366     },
44367
44368     onItemClick : function(item, index, e){
44369                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44370                         return false;
44371                 }
44372                 return true;
44373     },
44374
44375     unselect : function(nodeInfo, suppressEvent){
44376                 var node = this.getNode(nodeInfo);
44377                 if(node && this.isSelected(node)){
44378                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44379                                 Roo.fly(node).removeClass(this.selectedClass);
44380                                 this.selections.remove(node);
44381                                 if(!suppressEvent){
44382                                         this.fireEvent("selectionchange", this, this.selections);
44383                                 }
44384                         }
44385                 }
44386     }
44387 });
44388 /*
44389  * Based on:
44390  * Ext JS Library 1.1.1
44391  * Copyright(c) 2006-2007, Ext JS, LLC.
44392  *
44393  * Originally Released Under LGPL - original licence link has changed is not relivant.
44394  *
44395  * Fork - LGPL
44396  * <script type="text/javascript">
44397  */
44398  
44399 /**
44400  * @class Roo.LayoutManager
44401  * @extends Roo.util.Observable
44402  * Base class for layout managers.
44403  */
44404 Roo.LayoutManager = function(container, config){
44405     Roo.LayoutManager.superclass.constructor.call(this);
44406     this.el = Roo.get(container);
44407     // ie scrollbar fix
44408     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44409         document.body.scroll = "no";
44410     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44411         this.el.position('relative');
44412     }
44413     this.id = this.el.id;
44414     this.el.addClass("x-layout-container");
44415     /** false to disable window resize monitoring @type Boolean */
44416     this.monitorWindowResize = true;
44417     this.regions = {};
44418     this.addEvents({
44419         /**
44420          * @event layout
44421          * Fires when a layout is performed. 
44422          * @param {Roo.LayoutManager} this
44423          */
44424         "layout" : true,
44425         /**
44426          * @event regionresized
44427          * Fires when the user resizes a region. 
44428          * @param {Roo.LayoutRegion} region The resized region
44429          * @param {Number} newSize The new size (width for east/west, height for north/south)
44430          */
44431         "regionresized" : true,
44432         /**
44433          * @event regioncollapsed
44434          * Fires when a region is collapsed. 
44435          * @param {Roo.LayoutRegion} region The collapsed region
44436          */
44437         "regioncollapsed" : true,
44438         /**
44439          * @event regionexpanded
44440          * Fires when a region is expanded.  
44441          * @param {Roo.LayoutRegion} region The expanded region
44442          */
44443         "regionexpanded" : true
44444     });
44445     this.updating = false;
44446     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44447 };
44448
44449 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44450     /**
44451      * Returns true if this layout is currently being updated
44452      * @return {Boolean}
44453      */
44454     isUpdating : function(){
44455         return this.updating; 
44456     },
44457     
44458     /**
44459      * Suspend the LayoutManager from doing auto-layouts while
44460      * making multiple add or remove calls
44461      */
44462     beginUpdate : function(){
44463         this.updating = true;    
44464     },
44465     
44466     /**
44467      * Restore auto-layouts and optionally disable the manager from performing a layout
44468      * @param {Boolean} noLayout true to disable a layout update 
44469      */
44470     endUpdate : function(noLayout){
44471         this.updating = false;
44472         if(!noLayout){
44473             this.layout();
44474         }    
44475     },
44476     
44477     layout: function(){
44478         
44479     },
44480     
44481     onRegionResized : function(region, newSize){
44482         this.fireEvent("regionresized", region, newSize);
44483         this.layout();
44484     },
44485     
44486     onRegionCollapsed : function(region){
44487         this.fireEvent("regioncollapsed", region);
44488     },
44489     
44490     onRegionExpanded : function(region){
44491         this.fireEvent("regionexpanded", region);
44492     },
44493         
44494     /**
44495      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44496      * performs box-model adjustments.
44497      * @return {Object} The size as an object {width: (the width), height: (the height)}
44498      */
44499     getViewSize : function(){
44500         var size;
44501         if(this.el.dom != document.body){
44502             size = this.el.getSize();
44503         }else{
44504             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44505         }
44506         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44507         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44508         return size;
44509     },
44510     
44511     /**
44512      * Returns the Element this layout is bound to.
44513      * @return {Roo.Element}
44514      */
44515     getEl : function(){
44516         return this.el;
44517     },
44518     
44519     /**
44520      * Returns the specified region.
44521      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44522      * @return {Roo.LayoutRegion}
44523      */
44524     getRegion : function(target){
44525         return this.regions[target.toLowerCase()];
44526     },
44527     
44528     onWindowResize : function(){
44529         if(this.monitorWindowResize){
44530             this.layout();
44531         }
44532     }
44533 });/*
44534  * Based on:
44535  * Ext JS Library 1.1.1
44536  * Copyright(c) 2006-2007, Ext JS, LLC.
44537  *
44538  * Originally Released Under LGPL - original licence link has changed is not relivant.
44539  *
44540  * Fork - LGPL
44541  * <script type="text/javascript">
44542  */
44543 /**
44544  * @class Roo.BorderLayout
44545  * @extends Roo.LayoutManager
44546  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44547  * please see: <br><br>
44548  * <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>
44549  * <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>
44550  * Example:
44551  <pre><code>
44552  var layout = new Roo.BorderLayout(document.body, {
44553     north: {
44554         initialSize: 25,
44555         titlebar: false
44556     },
44557     west: {
44558         split:true,
44559         initialSize: 200,
44560         minSize: 175,
44561         maxSize: 400,
44562         titlebar: true,
44563         collapsible: true
44564     },
44565     east: {
44566         split:true,
44567         initialSize: 202,
44568         minSize: 175,
44569         maxSize: 400,
44570         titlebar: true,
44571         collapsible: true
44572     },
44573     south: {
44574         split:true,
44575         initialSize: 100,
44576         minSize: 100,
44577         maxSize: 200,
44578         titlebar: true,
44579         collapsible: true
44580     },
44581     center: {
44582         titlebar: true,
44583         autoScroll:true,
44584         resizeTabs: true,
44585         minTabWidth: 50,
44586         preferredTabWidth: 150
44587     }
44588 });
44589
44590 // shorthand
44591 var CP = Roo.ContentPanel;
44592
44593 layout.beginUpdate();
44594 layout.add("north", new CP("north", "North"));
44595 layout.add("south", new CP("south", {title: "South", closable: true}));
44596 layout.add("west", new CP("west", {title: "West"}));
44597 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44598 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44599 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44600 layout.getRegion("center").showPanel("center1");
44601 layout.endUpdate();
44602 </code></pre>
44603
44604 <b>The container the layout is rendered into can be either the body element or any other element.
44605 If it is not the body element, the container needs to either be an absolute positioned element,
44606 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44607 the container size if it is not the body element.</b>
44608
44609 * @constructor
44610 * Create a new BorderLayout
44611 * @param {String/HTMLElement/Element} container The container this layout is bound to
44612 * @param {Object} config Configuration options
44613  */
44614 Roo.BorderLayout = function(container, config){
44615     config = config || {};
44616     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44617     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44618     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44619         var target = this.factory.validRegions[i];
44620         if(config[target]){
44621             this.addRegion(target, config[target]);
44622         }
44623     }
44624 };
44625
44626 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44627     /**
44628      * Creates and adds a new region if it doesn't already exist.
44629      * @param {String} target The target region key (north, south, east, west or center).
44630      * @param {Object} config The regions config object
44631      * @return {BorderLayoutRegion} The new region
44632      */
44633     addRegion : function(target, config){
44634         if(!this.regions[target]){
44635             var r = this.factory.create(target, this, config);
44636             this.bindRegion(target, r);
44637         }
44638         return this.regions[target];
44639     },
44640
44641     // private (kinda)
44642     bindRegion : function(name, r){
44643         this.regions[name] = r;
44644         r.on("visibilitychange", this.layout, this);
44645         r.on("paneladded", this.layout, this);
44646         r.on("panelremoved", this.layout, this);
44647         r.on("invalidated", this.layout, this);
44648         r.on("resized", this.onRegionResized, this);
44649         r.on("collapsed", this.onRegionCollapsed, this);
44650         r.on("expanded", this.onRegionExpanded, this);
44651     },
44652
44653     /**
44654      * Performs a layout update.
44655      */
44656     layout : function(){
44657         if(this.updating) return;
44658         var size = this.getViewSize();
44659         var w = size.width;
44660         var h = size.height;
44661         var centerW = w;
44662         var centerH = h;
44663         var centerY = 0;
44664         var centerX = 0;
44665         //var x = 0, y = 0;
44666
44667         var rs = this.regions;
44668         var north = rs["north"];
44669         var south = rs["south"]; 
44670         var west = rs["west"];
44671         var east = rs["east"];
44672         var center = rs["center"];
44673         //if(this.hideOnLayout){ // not supported anymore
44674             //c.el.setStyle("display", "none");
44675         //}
44676         if(north && north.isVisible()){
44677             var b = north.getBox();
44678             var m = north.getMargins();
44679             b.width = w - (m.left+m.right);
44680             b.x = m.left;
44681             b.y = m.top;
44682             centerY = b.height + b.y + m.bottom;
44683             centerH -= centerY;
44684             north.updateBox(this.safeBox(b));
44685         }
44686         if(south && south.isVisible()){
44687             var b = south.getBox();
44688             var m = south.getMargins();
44689             b.width = w - (m.left+m.right);
44690             b.x = m.left;
44691             var totalHeight = (b.height + m.top + m.bottom);
44692             b.y = h - totalHeight + m.top;
44693             centerH -= totalHeight;
44694             south.updateBox(this.safeBox(b));
44695         }
44696         if(west && west.isVisible()){
44697             var b = west.getBox();
44698             var m = west.getMargins();
44699             b.height = centerH - (m.top+m.bottom);
44700             b.x = m.left;
44701             b.y = centerY + m.top;
44702             var totalWidth = (b.width + m.left + m.right);
44703             centerX += totalWidth;
44704             centerW -= totalWidth;
44705             west.updateBox(this.safeBox(b));
44706         }
44707         if(east && east.isVisible()){
44708             var b = east.getBox();
44709             var m = east.getMargins();
44710             b.height = centerH - (m.top+m.bottom);
44711             var totalWidth = (b.width + m.left + m.right);
44712             b.x = w - totalWidth + m.left;
44713             b.y = centerY + m.top;
44714             centerW -= totalWidth;
44715             east.updateBox(this.safeBox(b));
44716         }
44717         if(center){
44718             var m = center.getMargins();
44719             var centerBox = {
44720                 x: centerX + m.left,
44721                 y: centerY + m.top,
44722                 width: centerW - (m.left+m.right),
44723                 height: centerH - (m.top+m.bottom)
44724             };
44725             //if(this.hideOnLayout){
44726                 //center.el.setStyle("display", "block");
44727             //}
44728             center.updateBox(this.safeBox(centerBox));
44729         }
44730         this.el.repaint();
44731         this.fireEvent("layout", this);
44732     },
44733
44734     // private
44735     safeBox : function(box){
44736         box.width = Math.max(0, box.width);
44737         box.height = Math.max(0, box.height);
44738         return box;
44739     },
44740
44741     /**
44742      * Adds a ContentPanel (or subclass) to this layout.
44743      * @param {String} target The target region key (north, south, east, west or center).
44744      * @param {Roo.ContentPanel} panel The panel to add
44745      * @return {Roo.ContentPanel} The added panel
44746      */
44747     add : function(target, panel){
44748          
44749         target = target.toLowerCase();
44750         return this.regions[target].add(panel);
44751     },
44752
44753     /**
44754      * Remove a ContentPanel (or subclass) to this layout.
44755      * @param {String} target The target region key (north, south, east, west or center).
44756      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44757      * @return {Roo.ContentPanel} The removed panel
44758      */
44759     remove : function(target, panel){
44760         target = target.toLowerCase();
44761         return this.regions[target].remove(panel);
44762     },
44763
44764     /**
44765      * Searches all regions for a panel with the specified id
44766      * @param {String} panelId
44767      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44768      */
44769     findPanel : function(panelId){
44770         var rs = this.regions;
44771         for(var target in rs){
44772             if(typeof rs[target] != "function"){
44773                 var p = rs[target].getPanel(panelId);
44774                 if(p){
44775                     return p;
44776                 }
44777             }
44778         }
44779         return null;
44780     },
44781
44782     /**
44783      * Searches all regions for a panel with the specified id and activates (shows) it.
44784      * @param {String/ContentPanel} panelId The panels id or the panel itself
44785      * @return {Roo.ContentPanel} The shown panel or null
44786      */
44787     showPanel : function(panelId) {
44788       var rs = this.regions;
44789       for(var target in rs){
44790          var r = rs[target];
44791          if(typeof r != "function"){
44792             if(r.hasPanel(panelId)){
44793                return r.showPanel(panelId);
44794             }
44795          }
44796       }
44797       return null;
44798    },
44799
44800    /**
44801      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44802      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44803      */
44804     restoreState : function(provider){
44805         if(!provider){
44806             provider = Roo.state.Manager;
44807         }
44808         var sm = new Roo.LayoutStateManager();
44809         sm.init(this, provider);
44810     },
44811
44812     /**
44813      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44814      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44815      * a valid ContentPanel config object.  Example:
44816      * <pre><code>
44817 // Create the main layout
44818 var layout = new Roo.BorderLayout('main-ct', {
44819     west: {
44820         split:true,
44821         minSize: 175,
44822         titlebar: true
44823     },
44824     center: {
44825         title:'Components'
44826     }
44827 }, 'main-ct');
44828
44829 // Create and add multiple ContentPanels at once via configs
44830 layout.batchAdd({
44831    west: {
44832        id: 'source-files',
44833        autoCreate:true,
44834        title:'Ext Source Files',
44835        autoScroll:true,
44836        fitToFrame:true
44837    },
44838    center : {
44839        el: cview,
44840        autoScroll:true,
44841        fitToFrame:true,
44842        toolbar: tb,
44843        resizeEl:'cbody'
44844    }
44845 });
44846 </code></pre>
44847      * @param {Object} regions An object containing ContentPanel configs by region name
44848      */
44849     batchAdd : function(regions){
44850         this.beginUpdate();
44851         for(var rname in regions){
44852             var lr = this.regions[rname];
44853             if(lr){
44854                 this.addTypedPanels(lr, regions[rname]);
44855             }
44856         }
44857         this.endUpdate();
44858     },
44859
44860     // private
44861     addTypedPanels : function(lr, ps){
44862         if(typeof ps == 'string'){
44863             lr.add(new Roo.ContentPanel(ps));
44864         }
44865         else if(ps instanceof Array){
44866             for(var i =0, len = ps.length; i < len; i++){
44867                 this.addTypedPanels(lr, ps[i]);
44868             }
44869         }
44870         else if(!ps.events){ // raw config?
44871             var el = ps.el;
44872             delete ps.el; // prevent conflict
44873             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44874         }
44875         else {  // panel object assumed!
44876             lr.add(ps);
44877         }
44878     },
44879     /**
44880      * Adds a xtype elements to the layout.
44881      * <pre><code>
44882
44883 layout.addxtype({
44884        xtype : 'ContentPanel',
44885        region: 'west',
44886        items: [ .... ]
44887    }
44888 );
44889
44890 layout.addxtype({
44891         xtype : 'NestedLayoutPanel',
44892         region: 'west',
44893         layout: {
44894            center: { },
44895            west: { }   
44896         },
44897         items : [ ... list of content panels or nested layout panels.. ]
44898    }
44899 );
44900 </code></pre>
44901      * @param {Object} cfg Xtype definition of item to add.
44902      */
44903     addxtype : function(cfg)
44904     {
44905         // basically accepts a pannel...
44906         // can accept a layout region..!?!?
44907         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44908         
44909         if (!cfg.xtype.match(/Panel$/)) {
44910             return false;
44911         }
44912         var ret = false;
44913         
44914         if (typeof(cfg.region) == 'undefined') {
44915             Roo.log("Failed to add Panel, region was not set");
44916             Roo.log(cfg);
44917             return false;
44918         }
44919         var region = cfg.region;
44920         delete cfg.region;
44921         
44922           
44923         var xitems = [];
44924         if (cfg.items) {
44925             xitems = cfg.items;
44926             delete cfg.items;
44927         }
44928         var nb = false;
44929         
44930         switch(cfg.xtype) 
44931         {
44932             case 'ContentPanel':  // ContentPanel (el, cfg)
44933             case 'ScrollPanel':  // ContentPanel (el, cfg)
44934                 if(cfg.autoCreate) {
44935                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44936                 } else {
44937                     var el = this.el.createChild();
44938                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44939                 }
44940                 
44941                 this.add(region, ret);
44942                 break;
44943             
44944             
44945             case 'TreePanel': // our new panel!
44946                 cfg.el = this.el.createChild();
44947                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44948                 this.add(region, ret);
44949                 break;
44950             
44951             case 'NestedLayoutPanel': 
44952                 // create a new Layout (which is  a Border Layout...
44953                 var el = this.el.createChild();
44954                 var clayout = cfg.layout;
44955                 delete cfg.layout;
44956                 clayout.items   = clayout.items  || [];
44957                 // replace this exitems with the clayout ones..
44958                 xitems = clayout.items;
44959                  
44960                 
44961                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44962                     cfg.background = false;
44963                 }
44964                 var layout = new Roo.BorderLayout(el, clayout);
44965                 
44966                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44967                 //console.log('adding nested layout panel '  + cfg.toSource());
44968                 this.add(region, ret);
44969                 nb = {}; /// find first...
44970                 break;
44971                 
44972             case 'GridPanel': 
44973             
44974                 // needs grid and region
44975                 
44976                 //var el = this.getRegion(region).el.createChild();
44977                 var el = this.el.createChild();
44978                 // create the grid first...
44979                 
44980                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44981                 delete cfg.grid;
44982                 if (region == 'center' && this.active ) {
44983                     cfg.background = false;
44984                 }
44985                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44986                 
44987                 this.add(region, ret);
44988                 if (cfg.background) {
44989                     ret.on('activate', function(gp) {
44990                         if (!gp.grid.rendered) {
44991                             gp.grid.render();
44992                         }
44993                     });
44994                 } else {
44995                     grid.render();
44996                 }
44997                 break;
44998            
44999                
45000                 
45001                 
45002             default: 
45003                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
45004                 return null;
45005              // GridPanel (grid, cfg)
45006             
45007         }
45008         this.beginUpdate();
45009         // add children..
45010         var region = '';
45011         var abn = {};
45012         Roo.each(xitems, function(i)  {
45013             region = nb && i.region ? i.region : false;
45014             
45015             var add = ret.addxtype(i);
45016            
45017             if (region) {
45018                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
45019                 if (!i.background) {
45020                     abn[region] = nb[region] ;
45021                 }
45022             }
45023             
45024         });
45025         this.endUpdate();
45026
45027         // make the last non-background panel active..
45028         //if (nb) { Roo.log(abn); }
45029         if (nb) {
45030             
45031             for(var r in abn) {
45032                 region = this.getRegion(r);
45033                 if (region) {
45034                     // tried using nb[r], but it does not work..
45035                      
45036                     region.showPanel(abn[r]);
45037                    
45038                 }
45039             }
45040         }
45041         return ret;
45042         
45043     }
45044 });
45045
45046 /**
45047  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
45048  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
45049  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
45050  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
45051  * <pre><code>
45052 // shorthand
45053 var CP = Roo.ContentPanel;
45054
45055 var layout = Roo.BorderLayout.create({
45056     north: {
45057         initialSize: 25,
45058         titlebar: false,
45059         panels: [new CP("north", "North")]
45060     },
45061     west: {
45062         split:true,
45063         initialSize: 200,
45064         minSize: 175,
45065         maxSize: 400,
45066         titlebar: true,
45067         collapsible: true,
45068         panels: [new CP("west", {title: "West"})]
45069     },
45070     east: {
45071         split:true,
45072         initialSize: 202,
45073         minSize: 175,
45074         maxSize: 400,
45075         titlebar: true,
45076         collapsible: true,
45077         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
45078     },
45079     south: {
45080         split:true,
45081         initialSize: 100,
45082         minSize: 100,
45083         maxSize: 200,
45084         titlebar: true,
45085         collapsible: true,
45086         panels: [new CP("south", {title: "South", closable: true})]
45087     },
45088     center: {
45089         titlebar: true,
45090         autoScroll:true,
45091         resizeTabs: true,
45092         minTabWidth: 50,
45093         preferredTabWidth: 150,
45094         panels: [
45095             new CP("center1", {title: "Close Me", closable: true}),
45096             new CP("center2", {title: "Center Panel", closable: false})
45097         ]
45098     }
45099 }, document.body);
45100
45101 layout.getRegion("center").showPanel("center1");
45102 </code></pre>
45103  * @param config
45104  * @param targetEl
45105  */
45106 Roo.BorderLayout.create = function(config, targetEl){
45107     var layout = new Roo.BorderLayout(targetEl || document.body, config);
45108     layout.beginUpdate();
45109     var regions = Roo.BorderLayout.RegionFactory.validRegions;
45110     for(var j = 0, jlen = regions.length; j < jlen; j++){
45111         var lr = regions[j];
45112         if(layout.regions[lr] && config[lr].panels){
45113             var r = layout.regions[lr];
45114             var ps = config[lr].panels;
45115             layout.addTypedPanels(r, ps);
45116         }
45117     }
45118     layout.endUpdate();
45119     return layout;
45120 };
45121
45122 // private
45123 Roo.BorderLayout.RegionFactory = {
45124     // private
45125     validRegions : ["north","south","east","west","center"],
45126
45127     // private
45128     create : function(target, mgr, config){
45129         target = target.toLowerCase();
45130         if(config.lightweight || config.basic){
45131             return new Roo.BasicLayoutRegion(mgr, config, target);
45132         }
45133         switch(target){
45134             case "north":
45135                 return new Roo.NorthLayoutRegion(mgr, config);
45136             case "south":
45137                 return new Roo.SouthLayoutRegion(mgr, config);
45138             case "east":
45139                 return new Roo.EastLayoutRegion(mgr, config);
45140             case "west":
45141                 return new Roo.WestLayoutRegion(mgr, config);
45142             case "center":
45143                 return new Roo.CenterLayoutRegion(mgr, config);
45144         }
45145         throw 'Layout region "'+target+'" not supported.';
45146     }
45147 };/*
45148  * Based on:
45149  * Ext JS Library 1.1.1
45150  * Copyright(c) 2006-2007, Ext JS, LLC.
45151  *
45152  * Originally Released Under LGPL - original licence link has changed is not relivant.
45153  *
45154  * Fork - LGPL
45155  * <script type="text/javascript">
45156  */
45157  
45158 /**
45159  * @class Roo.BasicLayoutRegion
45160  * @extends Roo.util.Observable
45161  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
45162  * and does not have a titlebar, tabs or any other features. All it does is size and position 
45163  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
45164  */
45165 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
45166     this.mgr = mgr;
45167     this.position  = pos;
45168     this.events = {
45169         /**
45170          * @scope Roo.BasicLayoutRegion
45171          */
45172         
45173         /**
45174          * @event beforeremove
45175          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45176          * @param {Roo.LayoutRegion} this
45177          * @param {Roo.ContentPanel} panel The panel
45178          * @param {Object} e The cancel event object
45179          */
45180         "beforeremove" : true,
45181         /**
45182          * @event invalidated
45183          * Fires when the layout for this region is changed.
45184          * @param {Roo.LayoutRegion} this
45185          */
45186         "invalidated" : true,
45187         /**
45188          * @event visibilitychange
45189          * Fires when this region is shown or hidden 
45190          * @param {Roo.LayoutRegion} this
45191          * @param {Boolean} visibility true or false
45192          */
45193         "visibilitychange" : true,
45194         /**
45195          * @event paneladded
45196          * Fires when a panel is added. 
45197          * @param {Roo.LayoutRegion} this
45198          * @param {Roo.ContentPanel} panel The panel
45199          */
45200         "paneladded" : true,
45201         /**
45202          * @event panelremoved
45203          * Fires when a panel is removed. 
45204          * @param {Roo.LayoutRegion} this
45205          * @param {Roo.ContentPanel} panel The panel
45206          */
45207         "panelremoved" : true,
45208         /**
45209          * @event collapsed
45210          * Fires when this region is collapsed.
45211          * @param {Roo.LayoutRegion} this
45212          */
45213         "collapsed" : true,
45214         /**
45215          * @event expanded
45216          * Fires when this region is expanded.
45217          * @param {Roo.LayoutRegion} this
45218          */
45219         "expanded" : true,
45220         /**
45221          * @event slideshow
45222          * Fires when this region is slid into view.
45223          * @param {Roo.LayoutRegion} this
45224          */
45225         "slideshow" : true,
45226         /**
45227          * @event slidehide
45228          * Fires when this region slides out of view. 
45229          * @param {Roo.LayoutRegion} this
45230          */
45231         "slidehide" : true,
45232         /**
45233          * @event panelactivated
45234          * Fires when a panel is activated. 
45235          * @param {Roo.LayoutRegion} this
45236          * @param {Roo.ContentPanel} panel The activated panel
45237          */
45238         "panelactivated" : true,
45239         /**
45240          * @event resized
45241          * Fires when the user resizes this region. 
45242          * @param {Roo.LayoutRegion} this
45243          * @param {Number} newSize The new size (width for east/west, height for north/south)
45244          */
45245         "resized" : true
45246     };
45247     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45248     this.panels = new Roo.util.MixedCollection();
45249     this.panels.getKey = this.getPanelId.createDelegate(this);
45250     this.box = null;
45251     this.activePanel = null;
45252     // ensure listeners are added...
45253     
45254     if (config.listeners || config.events) {
45255         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45256             listeners : config.listeners || {},
45257             events : config.events || {}
45258         });
45259     }
45260     
45261     if(skipConfig !== true){
45262         this.applyConfig(config);
45263     }
45264 };
45265
45266 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45267     getPanelId : function(p){
45268         return p.getId();
45269     },
45270     
45271     applyConfig : function(config){
45272         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45273         this.config = config;
45274         
45275     },
45276     
45277     /**
45278      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45279      * the width, for horizontal (north, south) the height.
45280      * @param {Number} newSize The new width or height
45281      */
45282     resizeTo : function(newSize){
45283         var el = this.el ? this.el :
45284                  (this.activePanel ? this.activePanel.getEl() : null);
45285         if(el){
45286             switch(this.position){
45287                 case "east":
45288                 case "west":
45289                     el.setWidth(newSize);
45290                     this.fireEvent("resized", this, newSize);
45291                 break;
45292                 case "north":
45293                 case "south":
45294                     el.setHeight(newSize);
45295                     this.fireEvent("resized", this, newSize);
45296                 break;                
45297             }
45298         }
45299     },
45300     
45301     getBox : function(){
45302         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45303     },
45304     
45305     getMargins : function(){
45306         return this.margins;
45307     },
45308     
45309     updateBox : function(box){
45310         this.box = box;
45311         var el = this.activePanel.getEl();
45312         el.dom.style.left = box.x + "px";
45313         el.dom.style.top = box.y + "px";
45314         this.activePanel.setSize(box.width, box.height);
45315     },
45316     
45317     /**
45318      * Returns the container element for this region.
45319      * @return {Roo.Element}
45320      */
45321     getEl : function(){
45322         return this.activePanel;
45323     },
45324     
45325     /**
45326      * Returns true if this region is currently visible.
45327      * @return {Boolean}
45328      */
45329     isVisible : function(){
45330         return this.activePanel ? true : false;
45331     },
45332     
45333     setActivePanel : function(panel){
45334         panel = this.getPanel(panel);
45335         if(this.activePanel && this.activePanel != panel){
45336             this.activePanel.setActiveState(false);
45337             this.activePanel.getEl().setLeftTop(-10000,-10000);
45338         }
45339         this.activePanel = panel;
45340         panel.setActiveState(true);
45341         if(this.box){
45342             panel.setSize(this.box.width, this.box.height);
45343         }
45344         this.fireEvent("panelactivated", this, panel);
45345         this.fireEvent("invalidated");
45346     },
45347     
45348     /**
45349      * Show the specified panel.
45350      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45351      * @return {Roo.ContentPanel} The shown panel or null
45352      */
45353     showPanel : function(panel){
45354         if(panel = this.getPanel(panel)){
45355             this.setActivePanel(panel);
45356         }
45357         return panel;
45358     },
45359     
45360     /**
45361      * Get the active panel for this region.
45362      * @return {Roo.ContentPanel} The active panel or null
45363      */
45364     getActivePanel : function(){
45365         return this.activePanel;
45366     },
45367     
45368     /**
45369      * Add the passed ContentPanel(s)
45370      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45371      * @return {Roo.ContentPanel} The panel added (if only one was added)
45372      */
45373     add : function(panel){
45374         if(arguments.length > 1){
45375             for(var i = 0, len = arguments.length; i < len; i++) {
45376                 this.add(arguments[i]);
45377             }
45378             return null;
45379         }
45380         if(this.hasPanel(panel)){
45381             this.showPanel(panel);
45382             return panel;
45383         }
45384         var el = panel.getEl();
45385         if(el.dom.parentNode != this.mgr.el.dom){
45386             this.mgr.el.dom.appendChild(el.dom);
45387         }
45388         if(panel.setRegion){
45389             panel.setRegion(this);
45390         }
45391         this.panels.add(panel);
45392         el.setStyle("position", "absolute");
45393         if(!panel.background){
45394             this.setActivePanel(panel);
45395             if(this.config.initialSize && this.panels.getCount()==1){
45396                 this.resizeTo(this.config.initialSize);
45397             }
45398         }
45399         this.fireEvent("paneladded", this, panel);
45400         return panel;
45401     },
45402     
45403     /**
45404      * Returns true if the panel is in this region.
45405      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45406      * @return {Boolean}
45407      */
45408     hasPanel : function(panel){
45409         if(typeof panel == "object"){ // must be panel obj
45410             panel = panel.getId();
45411         }
45412         return this.getPanel(panel) ? true : false;
45413     },
45414     
45415     /**
45416      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45417      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45418      * @param {Boolean} preservePanel Overrides the config preservePanel option
45419      * @return {Roo.ContentPanel} The panel that was removed
45420      */
45421     remove : function(panel, preservePanel){
45422         panel = this.getPanel(panel);
45423         if(!panel){
45424             return null;
45425         }
45426         var e = {};
45427         this.fireEvent("beforeremove", this, panel, e);
45428         if(e.cancel === true){
45429             return null;
45430         }
45431         var panelId = panel.getId();
45432         this.panels.removeKey(panelId);
45433         return panel;
45434     },
45435     
45436     /**
45437      * Returns the panel specified or null if it's not in this region.
45438      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45439      * @return {Roo.ContentPanel}
45440      */
45441     getPanel : function(id){
45442         if(typeof id == "object"){ // must be panel obj
45443             return id;
45444         }
45445         return this.panels.get(id);
45446     },
45447     
45448     /**
45449      * Returns this regions position (north/south/east/west/center).
45450      * @return {String} 
45451      */
45452     getPosition: function(){
45453         return this.position;    
45454     }
45455 });/*
45456  * Based on:
45457  * Ext JS Library 1.1.1
45458  * Copyright(c) 2006-2007, Ext JS, LLC.
45459  *
45460  * Originally Released Under LGPL - original licence link has changed is not relivant.
45461  *
45462  * Fork - LGPL
45463  * <script type="text/javascript">
45464  */
45465  
45466 /**
45467  * @class Roo.LayoutRegion
45468  * @extends Roo.BasicLayoutRegion
45469  * This class represents a region in a layout manager.
45470  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45471  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45472  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45473  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45474  * @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})
45475  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45476  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45477  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45478  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45479  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45480  * @cfg {String}    title           The title for the region (overrides panel titles)
45481  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45482  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45483  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45484  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45485  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45486  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45487  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45488  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45489  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45490  * @cfg {Boolean}   showPin         True to show a pin button
45491  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45492  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45493  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45494  * @cfg {Number}    width           For East/West panels
45495  * @cfg {Number}    height          For North/South panels
45496  * @cfg {Boolean}   split           To show the splitter
45497  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45498  */
45499 Roo.LayoutRegion = function(mgr, config, pos){
45500     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45501     var dh = Roo.DomHelper;
45502     /** This region's container element 
45503     * @type Roo.Element */
45504     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45505     /** This region's title element 
45506     * @type Roo.Element */
45507
45508     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45509         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45510         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45511     ]}, true);
45512     this.titleEl.enableDisplayMode();
45513     /** This region's title text element 
45514     * @type HTMLElement */
45515     this.titleTextEl = this.titleEl.dom.firstChild;
45516     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45517     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45518     this.closeBtn.enableDisplayMode();
45519     this.closeBtn.on("click", this.closeClicked, this);
45520     this.closeBtn.hide();
45521
45522     this.createBody(config);
45523     this.visible = true;
45524     this.collapsed = false;
45525
45526     if(config.hideWhenEmpty){
45527         this.hide();
45528         this.on("paneladded", this.validateVisibility, this);
45529         this.on("panelremoved", this.validateVisibility, this);
45530     }
45531     this.applyConfig(config);
45532 };
45533
45534 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45535
45536     createBody : function(){
45537         /** This region's body element 
45538         * @type Roo.Element */
45539         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45540     },
45541
45542     applyConfig : function(c){
45543         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45544             var dh = Roo.DomHelper;
45545             if(c.titlebar !== false){
45546                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45547                 this.collapseBtn.on("click", this.collapse, this);
45548                 this.collapseBtn.enableDisplayMode();
45549
45550                 if(c.showPin === true || this.showPin){
45551                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45552                     this.stickBtn.enableDisplayMode();
45553                     this.stickBtn.on("click", this.expand, this);
45554                     this.stickBtn.hide();
45555                 }
45556             }
45557             /** This region's collapsed element
45558             * @type Roo.Element */
45559             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45560                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45561             ]}, true);
45562             if(c.floatable !== false){
45563                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45564                this.collapsedEl.on("click", this.collapseClick, this);
45565             }
45566
45567             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45568                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45569                    id: "message", unselectable: "on", style:{"float":"left"}});
45570                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45571              }
45572             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45573             this.expandBtn.on("click", this.expand, this);
45574         }
45575         if(this.collapseBtn){
45576             this.collapseBtn.setVisible(c.collapsible == true);
45577         }
45578         this.cmargins = c.cmargins || this.cmargins ||
45579                          (this.position == "west" || this.position == "east" ?
45580                              {top: 0, left: 2, right:2, bottom: 0} :
45581                              {top: 2, left: 0, right:0, bottom: 2});
45582         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45583         this.bottomTabs = c.tabPosition != "top";
45584         this.autoScroll = c.autoScroll || false;
45585         if(this.autoScroll){
45586             this.bodyEl.setStyle("overflow", "auto");
45587         }else{
45588             this.bodyEl.setStyle("overflow", "hidden");
45589         }
45590         //if(c.titlebar !== false){
45591             if((!c.titlebar && !c.title) || c.titlebar === false){
45592                 this.titleEl.hide();
45593             }else{
45594                 this.titleEl.show();
45595                 if(c.title){
45596                     this.titleTextEl.innerHTML = c.title;
45597                 }
45598             }
45599         //}
45600         this.duration = c.duration || .30;
45601         this.slideDuration = c.slideDuration || .45;
45602         this.config = c;
45603         if(c.collapsed){
45604             this.collapse(true);
45605         }
45606         if(c.hidden){
45607             this.hide();
45608         }
45609     },
45610     /**
45611      * Returns true if this region is currently visible.
45612      * @return {Boolean}
45613      */
45614     isVisible : function(){
45615         return this.visible;
45616     },
45617
45618     /**
45619      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45620      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45621      */
45622     setCollapsedTitle : function(title){
45623         title = title || "&#160;";
45624         if(this.collapsedTitleTextEl){
45625             this.collapsedTitleTextEl.innerHTML = title;
45626         }
45627     },
45628
45629     getBox : function(){
45630         var b;
45631         if(!this.collapsed){
45632             b = this.el.getBox(false, true);
45633         }else{
45634             b = this.collapsedEl.getBox(false, true);
45635         }
45636         return b;
45637     },
45638
45639     getMargins : function(){
45640         return this.collapsed ? this.cmargins : this.margins;
45641     },
45642
45643     highlight : function(){
45644         this.el.addClass("x-layout-panel-dragover");
45645     },
45646
45647     unhighlight : function(){
45648         this.el.removeClass("x-layout-panel-dragover");
45649     },
45650
45651     updateBox : function(box){
45652         this.box = box;
45653         if(!this.collapsed){
45654             this.el.dom.style.left = box.x + "px";
45655             this.el.dom.style.top = box.y + "px";
45656             this.updateBody(box.width, box.height);
45657         }else{
45658             this.collapsedEl.dom.style.left = box.x + "px";
45659             this.collapsedEl.dom.style.top = box.y + "px";
45660             this.collapsedEl.setSize(box.width, box.height);
45661         }
45662         if(this.tabs){
45663             this.tabs.autoSizeTabs();
45664         }
45665     },
45666
45667     updateBody : function(w, h){
45668         if(w !== null){
45669             this.el.setWidth(w);
45670             w -= this.el.getBorderWidth("rl");
45671             if(this.config.adjustments){
45672                 w += this.config.adjustments[0];
45673             }
45674         }
45675         if(h !== null){
45676             this.el.setHeight(h);
45677             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45678             h -= this.el.getBorderWidth("tb");
45679             if(this.config.adjustments){
45680                 h += this.config.adjustments[1];
45681             }
45682             this.bodyEl.setHeight(h);
45683             if(this.tabs){
45684                 h = this.tabs.syncHeight(h);
45685             }
45686         }
45687         if(this.panelSize){
45688             w = w !== null ? w : this.panelSize.width;
45689             h = h !== null ? h : this.panelSize.height;
45690         }
45691         if(this.activePanel){
45692             var el = this.activePanel.getEl();
45693             w = w !== null ? w : el.getWidth();
45694             h = h !== null ? h : el.getHeight();
45695             this.panelSize = {width: w, height: h};
45696             this.activePanel.setSize(w, h);
45697         }
45698         if(Roo.isIE && this.tabs){
45699             this.tabs.el.repaint();
45700         }
45701     },
45702
45703     /**
45704      * Returns the container element for this region.
45705      * @return {Roo.Element}
45706      */
45707     getEl : function(){
45708         return this.el;
45709     },
45710
45711     /**
45712      * Hides this region.
45713      */
45714     hide : function(){
45715         if(!this.collapsed){
45716             this.el.dom.style.left = "-2000px";
45717             this.el.hide();
45718         }else{
45719             this.collapsedEl.dom.style.left = "-2000px";
45720             this.collapsedEl.hide();
45721         }
45722         this.visible = false;
45723         this.fireEvent("visibilitychange", this, false);
45724     },
45725
45726     /**
45727      * Shows this region if it was previously hidden.
45728      */
45729     show : function(){
45730         if(!this.collapsed){
45731             this.el.show();
45732         }else{
45733             this.collapsedEl.show();
45734         }
45735         this.visible = true;
45736         this.fireEvent("visibilitychange", this, true);
45737     },
45738
45739     closeClicked : function(){
45740         if(this.activePanel){
45741             this.remove(this.activePanel);
45742         }
45743     },
45744
45745     collapseClick : function(e){
45746         if(this.isSlid){
45747            e.stopPropagation();
45748            this.slideIn();
45749         }else{
45750            e.stopPropagation();
45751            this.slideOut();
45752         }
45753     },
45754
45755     /**
45756      * Collapses this region.
45757      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45758      */
45759     collapse : function(skipAnim){
45760         if(this.collapsed) return;
45761         this.collapsed = true;
45762         if(this.split){
45763             this.split.el.hide();
45764         }
45765         if(this.config.animate && skipAnim !== true){
45766             this.fireEvent("invalidated", this);
45767             this.animateCollapse();
45768         }else{
45769             this.el.setLocation(-20000,-20000);
45770             this.el.hide();
45771             this.collapsedEl.show();
45772             this.fireEvent("collapsed", this);
45773             this.fireEvent("invalidated", this);
45774         }
45775     },
45776
45777     animateCollapse : function(){
45778         // overridden
45779     },
45780
45781     /**
45782      * Expands this region if it was previously collapsed.
45783      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45784      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45785      */
45786     expand : function(e, skipAnim){
45787         if(e) e.stopPropagation();
45788         if(!this.collapsed || this.el.hasActiveFx()) return;
45789         if(this.isSlid){
45790             this.afterSlideIn();
45791             skipAnim = true;
45792         }
45793         this.collapsed = false;
45794         if(this.config.animate && skipAnim !== true){
45795             this.animateExpand();
45796         }else{
45797             this.el.show();
45798             if(this.split){
45799                 this.split.el.show();
45800             }
45801             this.collapsedEl.setLocation(-2000,-2000);
45802             this.collapsedEl.hide();
45803             this.fireEvent("invalidated", this);
45804             this.fireEvent("expanded", this);
45805         }
45806     },
45807
45808     animateExpand : function(){
45809         // overridden
45810     },
45811
45812     initTabs : function()
45813     {
45814         this.bodyEl.setStyle("overflow", "hidden");
45815         var ts = new Roo.TabPanel(
45816                 this.bodyEl.dom,
45817                 {
45818                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45819                     disableTooltips: this.config.disableTabTips,
45820                     toolbar : this.config.toolbar
45821                 }
45822         );
45823         if(this.config.hideTabs){
45824             ts.stripWrap.setDisplayed(false);
45825         }
45826         this.tabs = ts;
45827         ts.resizeTabs = this.config.resizeTabs === true;
45828         ts.minTabWidth = this.config.minTabWidth || 40;
45829         ts.maxTabWidth = this.config.maxTabWidth || 250;
45830         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45831         ts.monitorResize = false;
45832         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45833         ts.bodyEl.addClass('x-layout-tabs-body');
45834         this.panels.each(this.initPanelAsTab, this);
45835     },
45836
45837     initPanelAsTab : function(panel){
45838         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45839                     this.config.closeOnTab && panel.isClosable());
45840         if(panel.tabTip !== undefined){
45841             ti.setTooltip(panel.tabTip);
45842         }
45843         ti.on("activate", function(){
45844               this.setActivePanel(panel);
45845         }, this);
45846         if(this.config.closeOnTab){
45847             ti.on("beforeclose", function(t, e){
45848                 e.cancel = true;
45849                 this.remove(panel);
45850             }, this);
45851         }
45852         return ti;
45853     },
45854
45855     updatePanelTitle : function(panel, title){
45856         if(this.activePanel == panel){
45857             this.updateTitle(title);
45858         }
45859         if(this.tabs){
45860             var ti = this.tabs.getTab(panel.getEl().id);
45861             ti.setText(title);
45862             if(panel.tabTip !== undefined){
45863                 ti.setTooltip(panel.tabTip);
45864             }
45865         }
45866     },
45867
45868     updateTitle : function(title){
45869         if(this.titleTextEl && !this.config.title){
45870             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45871         }
45872     },
45873
45874     setActivePanel : function(panel){
45875         panel = this.getPanel(panel);
45876         if(this.activePanel && this.activePanel != panel){
45877             this.activePanel.setActiveState(false);
45878         }
45879         this.activePanel = panel;
45880         panel.setActiveState(true);
45881         if(this.panelSize){
45882             panel.setSize(this.panelSize.width, this.panelSize.height);
45883         }
45884         if(this.closeBtn){
45885             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45886         }
45887         this.updateTitle(panel.getTitle());
45888         if(this.tabs){
45889             this.fireEvent("invalidated", this);
45890         }
45891         this.fireEvent("panelactivated", this, panel);
45892     },
45893
45894     /**
45895      * Shows the specified panel.
45896      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45897      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45898      */
45899     showPanel : function(panel){
45900         if(panel = this.getPanel(panel)){
45901             if(this.tabs){
45902                 var tab = this.tabs.getTab(panel.getEl().id);
45903                 if(tab.isHidden()){
45904                     this.tabs.unhideTab(tab.id);
45905                 }
45906                 tab.activate();
45907             }else{
45908                 this.setActivePanel(panel);
45909             }
45910         }
45911         return panel;
45912     },
45913
45914     /**
45915      * Get the active panel for this region.
45916      * @return {Roo.ContentPanel} The active panel or null
45917      */
45918     getActivePanel : function(){
45919         return this.activePanel;
45920     },
45921
45922     validateVisibility : function(){
45923         if(this.panels.getCount() < 1){
45924             this.updateTitle("&#160;");
45925             this.closeBtn.hide();
45926             this.hide();
45927         }else{
45928             if(!this.isVisible()){
45929                 this.show();
45930             }
45931         }
45932     },
45933
45934     /**
45935      * Adds the passed ContentPanel(s) to this region.
45936      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45937      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45938      */
45939     add : function(panel){
45940         if(arguments.length > 1){
45941             for(var i = 0, len = arguments.length; i < len; i++) {
45942                 this.add(arguments[i]);
45943             }
45944             return null;
45945         }
45946         if(this.hasPanel(panel)){
45947             this.showPanel(panel);
45948             return panel;
45949         }
45950         panel.setRegion(this);
45951         this.panels.add(panel);
45952         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45953             this.bodyEl.dom.appendChild(panel.getEl().dom);
45954             if(panel.background !== true){
45955                 this.setActivePanel(panel);
45956             }
45957             this.fireEvent("paneladded", this, panel);
45958             return panel;
45959         }
45960         if(!this.tabs){
45961             this.initTabs();
45962         }else{
45963             this.initPanelAsTab(panel);
45964         }
45965         if(panel.background !== true){
45966             this.tabs.activate(panel.getEl().id);
45967         }
45968         this.fireEvent("paneladded", this, panel);
45969         return panel;
45970     },
45971
45972     /**
45973      * Hides the tab for the specified panel.
45974      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45975      */
45976     hidePanel : function(panel){
45977         if(this.tabs && (panel = this.getPanel(panel))){
45978             this.tabs.hideTab(panel.getEl().id);
45979         }
45980     },
45981
45982     /**
45983      * Unhides the tab for a previously hidden panel.
45984      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45985      */
45986     unhidePanel : function(panel){
45987         if(this.tabs && (panel = this.getPanel(panel))){
45988             this.tabs.unhideTab(panel.getEl().id);
45989         }
45990     },
45991
45992     clearPanels : function(){
45993         while(this.panels.getCount() > 0){
45994              this.remove(this.panels.first());
45995         }
45996     },
45997
45998     /**
45999      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
46000      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46001      * @param {Boolean} preservePanel Overrides the config preservePanel option
46002      * @return {Roo.ContentPanel} The panel that was removed
46003      */
46004     remove : function(panel, preservePanel){
46005         panel = this.getPanel(panel);
46006         if(!panel){
46007             return null;
46008         }
46009         var e = {};
46010         this.fireEvent("beforeremove", this, panel, e);
46011         if(e.cancel === true){
46012             return null;
46013         }
46014         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
46015         var panelId = panel.getId();
46016         this.panels.removeKey(panelId);
46017         if(preservePanel){
46018             document.body.appendChild(panel.getEl().dom);
46019         }
46020         if(this.tabs){
46021             this.tabs.removeTab(panel.getEl().id);
46022         }else if (!preservePanel){
46023             this.bodyEl.dom.removeChild(panel.getEl().dom);
46024         }
46025         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
46026             var p = this.panels.first();
46027             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
46028             tempEl.appendChild(p.getEl().dom);
46029             this.bodyEl.update("");
46030             this.bodyEl.dom.appendChild(p.getEl().dom);
46031             tempEl = null;
46032             this.updateTitle(p.getTitle());
46033             this.tabs = null;
46034             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
46035             this.setActivePanel(p);
46036         }
46037         panel.setRegion(null);
46038         if(this.activePanel == panel){
46039             this.activePanel = null;
46040         }
46041         if(this.config.autoDestroy !== false && preservePanel !== true){
46042             try{panel.destroy();}catch(e){}
46043         }
46044         this.fireEvent("panelremoved", this, panel);
46045         return panel;
46046     },
46047
46048     /**
46049      * Returns the TabPanel component used by this region
46050      * @return {Roo.TabPanel}
46051      */
46052     getTabs : function(){
46053         return this.tabs;
46054     },
46055
46056     createTool : function(parentEl, className){
46057         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
46058             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
46059         btn.addClassOnOver("x-layout-tools-button-over");
46060         return btn;
46061     }
46062 });/*
46063  * Based on:
46064  * Ext JS Library 1.1.1
46065  * Copyright(c) 2006-2007, Ext JS, LLC.
46066  *
46067  * Originally Released Under LGPL - original licence link has changed is not relivant.
46068  *
46069  * Fork - LGPL
46070  * <script type="text/javascript">
46071  */
46072  
46073
46074
46075 /**
46076  * @class Roo.SplitLayoutRegion
46077  * @extends Roo.LayoutRegion
46078  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
46079  */
46080 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
46081     this.cursor = cursor;
46082     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
46083 };
46084
46085 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
46086     splitTip : "Drag to resize.",
46087     collapsibleSplitTip : "Drag to resize. Double click to hide.",
46088     useSplitTips : false,
46089
46090     applyConfig : function(config){
46091         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
46092         if(config.split){
46093             if(!this.split){
46094                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
46095                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
46096                 /** The SplitBar for this region 
46097                 * @type Roo.SplitBar */
46098                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
46099                 this.split.on("moved", this.onSplitMove, this);
46100                 this.split.useShim = config.useShim === true;
46101                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
46102                 if(this.useSplitTips){
46103                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
46104                 }
46105                 if(config.collapsible){
46106                     this.split.el.on("dblclick", this.collapse,  this);
46107                 }
46108             }
46109             if(typeof config.minSize != "undefined"){
46110                 this.split.minSize = config.minSize;
46111             }
46112             if(typeof config.maxSize != "undefined"){
46113                 this.split.maxSize = config.maxSize;
46114             }
46115             if(config.hideWhenEmpty || config.hidden || config.collapsed){
46116                 this.hideSplitter();
46117             }
46118         }
46119     },
46120
46121     getHMaxSize : function(){
46122          var cmax = this.config.maxSize || 10000;
46123          var center = this.mgr.getRegion("center");
46124          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
46125     },
46126
46127     getVMaxSize : function(){
46128          var cmax = this.config.maxSize || 10000;
46129          var center = this.mgr.getRegion("center");
46130          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
46131     },
46132
46133     onSplitMove : function(split, newSize){
46134         this.fireEvent("resized", this, newSize);
46135     },
46136     
46137     /** 
46138      * Returns the {@link Roo.SplitBar} for this region.
46139      * @return {Roo.SplitBar}
46140      */
46141     getSplitBar : function(){
46142         return this.split;
46143     },
46144     
46145     hide : function(){
46146         this.hideSplitter();
46147         Roo.SplitLayoutRegion.superclass.hide.call(this);
46148     },
46149
46150     hideSplitter : function(){
46151         if(this.split){
46152             this.split.el.setLocation(-2000,-2000);
46153             this.split.el.hide();
46154         }
46155     },
46156
46157     show : function(){
46158         if(this.split){
46159             this.split.el.show();
46160         }
46161         Roo.SplitLayoutRegion.superclass.show.call(this);
46162     },
46163     
46164     beforeSlide: function(){
46165         if(Roo.isGecko){// firefox overflow auto bug workaround
46166             this.bodyEl.clip();
46167             if(this.tabs) this.tabs.bodyEl.clip();
46168             if(this.activePanel){
46169                 this.activePanel.getEl().clip();
46170                 
46171                 if(this.activePanel.beforeSlide){
46172                     this.activePanel.beforeSlide();
46173                 }
46174             }
46175         }
46176     },
46177     
46178     afterSlide : function(){
46179         if(Roo.isGecko){// firefox overflow auto bug workaround
46180             this.bodyEl.unclip();
46181             if(this.tabs) this.tabs.bodyEl.unclip();
46182             if(this.activePanel){
46183                 this.activePanel.getEl().unclip();
46184                 if(this.activePanel.afterSlide){
46185                     this.activePanel.afterSlide();
46186                 }
46187             }
46188         }
46189     },
46190
46191     initAutoHide : function(){
46192         if(this.autoHide !== false){
46193             if(!this.autoHideHd){
46194                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46195                 this.autoHideHd = {
46196                     "mouseout": function(e){
46197                         if(!e.within(this.el, true)){
46198                             st.delay(500);
46199                         }
46200                     },
46201                     "mouseover" : function(e){
46202                         st.cancel();
46203                     },
46204                     scope : this
46205                 };
46206             }
46207             this.el.on(this.autoHideHd);
46208         }
46209     },
46210
46211     clearAutoHide : function(){
46212         if(this.autoHide !== false){
46213             this.el.un("mouseout", this.autoHideHd.mouseout);
46214             this.el.un("mouseover", this.autoHideHd.mouseover);
46215         }
46216     },
46217
46218     clearMonitor : function(){
46219         Roo.get(document).un("click", this.slideInIf, this);
46220     },
46221
46222     // these names are backwards but not changed for compat
46223     slideOut : function(){
46224         if(this.isSlid || this.el.hasActiveFx()){
46225             return;
46226         }
46227         this.isSlid = true;
46228         if(this.collapseBtn){
46229             this.collapseBtn.hide();
46230         }
46231         this.closeBtnState = this.closeBtn.getStyle('display');
46232         this.closeBtn.hide();
46233         if(this.stickBtn){
46234             this.stickBtn.show();
46235         }
46236         this.el.show();
46237         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46238         this.beforeSlide();
46239         this.el.setStyle("z-index", 10001);
46240         this.el.slideIn(this.getSlideAnchor(), {
46241             callback: function(){
46242                 this.afterSlide();
46243                 this.initAutoHide();
46244                 Roo.get(document).on("click", this.slideInIf, this);
46245                 this.fireEvent("slideshow", this);
46246             },
46247             scope: this,
46248             block: true
46249         });
46250     },
46251
46252     afterSlideIn : function(){
46253         this.clearAutoHide();
46254         this.isSlid = false;
46255         this.clearMonitor();
46256         this.el.setStyle("z-index", "");
46257         if(this.collapseBtn){
46258             this.collapseBtn.show();
46259         }
46260         this.closeBtn.setStyle('display', this.closeBtnState);
46261         if(this.stickBtn){
46262             this.stickBtn.hide();
46263         }
46264         this.fireEvent("slidehide", this);
46265     },
46266
46267     slideIn : function(cb){
46268         if(!this.isSlid || this.el.hasActiveFx()){
46269             Roo.callback(cb);
46270             return;
46271         }
46272         this.isSlid = false;
46273         this.beforeSlide();
46274         this.el.slideOut(this.getSlideAnchor(), {
46275             callback: function(){
46276                 this.el.setLeftTop(-10000, -10000);
46277                 this.afterSlide();
46278                 this.afterSlideIn();
46279                 Roo.callback(cb);
46280             },
46281             scope: this,
46282             block: true
46283         });
46284     },
46285     
46286     slideInIf : function(e){
46287         if(!e.within(this.el)){
46288             this.slideIn();
46289         }
46290     },
46291
46292     animateCollapse : function(){
46293         this.beforeSlide();
46294         this.el.setStyle("z-index", 20000);
46295         var anchor = this.getSlideAnchor();
46296         this.el.slideOut(anchor, {
46297             callback : function(){
46298                 this.el.setStyle("z-index", "");
46299                 this.collapsedEl.slideIn(anchor, {duration:.3});
46300                 this.afterSlide();
46301                 this.el.setLocation(-10000,-10000);
46302                 this.el.hide();
46303                 this.fireEvent("collapsed", this);
46304             },
46305             scope: this,
46306             block: true
46307         });
46308     },
46309
46310     animateExpand : function(){
46311         this.beforeSlide();
46312         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46313         this.el.setStyle("z-index", 20000);
46314         this.collapsedEl.hide({
46315             duration:.1
46316         });
46317         this.el.slideIn(this.getSlideAnchor(), {
46318             callback : function(){
46319                 this.el.setStyle("z-index", "");
46320                 this.afterSlide();
46321                 if(this.split){
46322                     this.split.el.show();
46323                 }
46324                 this.fireEvent("invalidated", this);
46325                 this.fireEvent("expanded", this);
46326             },
46327             scope: this,
46328             block: true
46329         });
46330     },
46331
46332     anchors : {
46333         "west" : "left",
46334         "east" : "right",
46335         "north" : "top",
46336         "south" : "bottom"
46337     },
46338
46339     sanchors : {
46340         "west" : "l",
46341         "east" : "r",
46342         "north" : "t",
46343         "south" : "b"
46344     },
46345
46346     canchors : {
46347         "west" : "tl-tr",
46348         "east" : "tr-tl",
46349         "north" : "tl-bl",
46350         "south" : "bl-tl"
46351     },
46352
46353     getAnchor : function(){
46354         return this.anchors[this.position];
46355     },
46356
46357     getCollapseAnchor : function(){
46358         return this.canchors[this.position];
46359     },
46360
46361     getSlideAnchor : function(){
46362         return this.sanchors[this.position];
46363     },
46364
46365     getAlignAdj : function(){
46366         var cm = this.cmargins;
46367         switch(this.position){
46368             case "west":
46369                 return [0, 0];
46370             break;
46371             case "east":
46372                 return [0, 0];
46373             break;
46374             case "north":
46375                 return [0, 0];
46376             break;
46377             case "south":
46378                 return [0, 0];
46379             break;
46380         }
46381     },
46382
46383     getExpandAdj : function(){
46384         var c = this.collapsedEl, cm = this.cmargins;
46385         switch(this.position){
46386             case "west":
46387                 return [-(cm.right+c.getWidth()+cm.left), 0];
46388             break;
46389             case "east":
46390                 return [cm.right+c.getWidth()+cm.left, 0];
46391             break;
46392             case "north":
46393                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46394             break;
46395             case "south":
46396                 return [0, cm.top+cm.bottom+c.getHeight()];
46397             break;
46398         }
46399     }
46400 });/*
46401  * Based on:
46402  * Ext JS Library 1.1.1
46403  * Copyright(c) 2006-2007, Ext JS, LLC.
46404  *
46405  * Originally Released Under LGPL - original licence link has changed is not relivant.
46406  *
46407  * Fork - LGPL
46408  * <script type="text/javascript">
46409  */
46410 /*
46411  * These classes are private internal classes
46412  */
46413 Roo.CenterLayoutRegion = function(mgr, config){
46414     Roo.LayoutRegion.call(this, mgr, config, "center");
46415     this.visible = true;
46416     this.minWidth = config.minWidth || 20;
46417     this.minHeight = config.minHeight || 20;
46418 };
46419
46420 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46421     hide : function(){
46422         // center panel can't be hidden
46423     },
46424     
46425     show : function(){
46426         // center panel can't be hidden
46427     },
46428     
46429     getMinWidth: function(){
46430         return this.minWidth;
46431     },
46432     
46433     getMinHeight: function(){
46434         return this.minHeight;
46435     }
46436 });
46437
46438
46439 Roo.NorthLayoutRegion = function(mgr, config){
46440     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46441     if(this.split){
46442         this.split.placement = Roo.SplitBar.TOP;
46443         this.split.orientation = Roo.SplitBar.VERTICAL;
46444         this.split.el.addClass("x-layout-split-v");
46445     }
46446     var size = config.initialSize || config.height;
46447     if(typeof size != "undefined"){
46448         this.el.setHeight(size);
46449     }
46450 };
46451 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46452     orientation: Roo.SplitBar.VERTICAL,
46453     getBox : function(){
46454         if(this.collapsed){
46455             return this.collapsedEl.getBox();
46456         }
46457         var box = this.el.getBox();
46458         if(this.split){
46459             box.height += this.split.el.getHeight();
46460         }
46461         return box;
46462     },
46463     
46464     updateBox : function(box){
46465         if(this.split && !this.collapsed){
46466             box.height -= this.split.el.getHeight();
46467             this.split.el.setLeft(box.x);
46468             this.split.el.setTop(box.y+box.height);
46469             this.split.el.setWidth(box.width);
46470         }
46471         if(this.collapsed){
46472             this.updateBody(box.width, null);
46473         }
46474         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46475     }
46476 });
46477
46478 Roo.SouthLayoutRegion = function(mgr, config){
46479     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46480     if(this.split){
46481         this.split.placement = Roo.SplitBar.BOTTOM;
46482         this.split.orientation = Roo.SplitBar.VERTICAL;
46483         this.split.el.addClass("x-layout-split-v");
46484     }
46485     var size = config.initialSize || config.height;
46486     if(typeof size != "undefined"){
46487         this.el.setHeight(size);
46488     }
46489 };
46490 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46491     orientation: Roo.SplitBar.VERTICAL,
46492     getBox : function(){
46493         if(this.collapsed){
46494             return this.collapsedEl.getBox();
46495         }
46496         var box = this.el.getBox();
46497         if(this.split){
46498             var sh = this.split.el.getHeight();
46499             box.height += sh;
46500             box.y -= sh;
46501         }
46502         return box;
46503     },
46504     
46505     updateBox : function(box){
46506         if(this.split && !this.collapsed){
46507             var sh = this.split.el.getHeight();
46508             box.height -= sh;
46509             box.y += sh;
46510             this.split.el.setLeft(box.x);
46511             this.split.el.setTop(box.y-sh);
46512             this.split.el.setWidth(box.width);
46513         }
46514         if(this.collapsed){
46515             this.updateBody(box.width, null);
46516         }
46517         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46518     }
46519 });
46520
46521 Roo.EastLayoutRegion = function(mgr, config){
46522     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46523     if(this.split){
46524         this.split.placement = Roo.SplitBar.RIGHT;
46525         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46526         this.split.el.addClass("x-layout-split-h");
46527     }
46528     var size = config.initialSize || config.width;
46529     if(typeof size != "undefined"){
46530         this.el.setWidth(size);
46531     }
46532 };
46533 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46534     orientation: Roo.SplitBar.HORIZONTAL,
46535     getBox : function(){
46536         if(this.collapsed){
46537             return this.collapsedEl.getBox();
46538         }
46539         var box = this.el.getBox();
46540         if(this.split){
46541             var sw = this.split.el.getWidth();
46542             box.width += sw;
46543             box.x -= sw;
46544         }
46545         return box;
46546     },
46547
46548     updateBox : function(box){
46549         if(this.split && !this.collapsed){
46550             var sw = this.split.el.getWidth();
46551             box.width -= sw;
46552             this.split.el.setLeft(box.x);
46553             this.split.el.setTop(box.y);
46554             this.split.el.setHeight(box.height);
46555             box.x += sw;
46556         }
46557         if(this.collapsed){
46558             this.updateBody(null, box.height);
46559         }
46560         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46561     }
46562 });
46563
46564 Roo.WestLayoutRegion = function(mgr, config){
46565     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46566     if(this.split){
46567         this.split.placement = Roo.SplitBar.LEFT;
46568         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46569         this.split.el.addClass("x-layout-split-h");
46570     }
46571     var size = config.initialSize || config.width;
46572     if(typeof size != "undefined"){
46573         this.el.setWidth(size);
46574     }
46575 };
46576 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46577     orientation: Roo.SplitBar.HORIZONTAL,
46578     getBox : function(){
46579         if(this.collapsed){
46580             return this.collapsedEl.getBox();
46581         }
46582         var box = this.el.getBox();
46583         if(this.split){
46584             box.width += this.split.el.getWidth();
46585         }
46586         return box;
46587     },
46588     
46589     updateBox : function(box){
46590         if(this.split && !this.collapsed){
46591             var sw = this.split.el.getWidth();
46592             box.width -= sw;
46593             this.split.el.setLeft(box.x+box.width);
46594             this.split.el.setTop(box.y);
46595             this.split.el.setHeight(box.height);
46596         }
46597         if(this.collapsed){
46598             this.updateBody(null, box.height);
46599         }
46600         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46601     }
46602 });
46603 /*
46604  * Based on:
46605  * Ext JS Library 1.1.1
46606  * Copyright(c) 2006-2007, Ext JS, LLC.
46607  *
46608  * Originally Released Under LGPL - original licence link has changed is not relivant.
46609  *
46610  * Fork - LGPL
46611  * <script type="text/javascript">
46612  */
46613  
46614  
46615 /*
46616  * Private internal class for reading and applying state
46617  */
46618 Roo.LayoutStateManager = function(layout){
46619      // default empty state
46620      this.state = {
46621         north: {},
46622         south: {},
46623         east: {},
46624         west: {}       
46625     };
46626 };
46627
46628 Roo.LayoutStateManager.prototype = {
46629     init : function(layout, provider){
46630         this.provider = provider;
46631         var state = provider.get(layout.id+"-layout-state");
46632         if(state){
46633             var wasUpdating = layout.isUpdating();
46634             if(!wasUpdating){
46635                 layout.beginUpdate();
46636             }
46637             for(var key in state){
46638                 if(typeof state[key] != "function"){
46639                     var rstate = state[key];
46640                     var r = layout.getRegion(key);
46641                     if(r && rstate){
46642                         if(rstate.size){
46643                             r.resizeTo(rstate.size);
46644                         }
46645                         if(rstate.collapsed == true){
46646                             r.collapse(true);
46647                         }else{
46648                             r.expand(null, true);
46649                         }
46650                     }
46651                 }
46652             }
46653             if(!wasUpdating){
46654                 layout.endUpdate();
46655             }
46656             this.state = state; 
46657         }
46658         this.layout = layout;
46659         layout.on("regionresized", this.onRegionResized, this);
46660         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46661         layout.on("regionexpanded", this.onRegionExpanded, this);
46662     },
46663     
46664     storeState : function(){
46665         this.provider.set(this.layout.id+"-layout-state", this.state);
46666     },
46667     
46668     onRegionResized : function(region, newSize){
46669         this.state[region.getPosition()].size = newSize;
46670         this.storeState();
46671     },
46672     
46673     onRegionCollapsed : function(region){
46674         this.state[region.getPosition()].collapsed = true;
46675         this.storeState();
46676     },
46677     
46678     onRegionExpanded : function(region){
46679         this.state[region.getPosition()].collapsed = false;
46680         this.storeState();
46681     }
46682 };/*
46683  * Based on:
46684  * Ext JS Library 1.1.1
46685  * Copyright(c) 2006-2007, Ext JS, LLC.
46686  *
46687  * Originally Released Under LGPL - original licence link has changed is not relivant.
46688  *
46689  * Fork - LGPL
46690  * <script type="text/javascript">
46691  */
46692 /**
46693  * @class Roo.ContentPanel
46694  * @extends Roo.util.Observable
46695  * A basic ContentPanel element.
46696  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46697  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46698  * @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
46699  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46700  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46701  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46702  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46703  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46704  * @cfg {String} title          The title for this panel
46705  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46706  * @cfg {String} url            Calls {@link #setUrl} with this value
46707  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46708  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46709  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46710  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46711
46712  * @constructor
46713  * Create a new ContentPanel.
46714  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46715  * @param {String/Object} config A string to set only the title or a config object
46716  * @param {String} content (optional) Set the HTML content for this panel
46717  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46718  */
46719 Roo.ContentPanel = function(el, config, content){
46720     
46721      
46722     /*
46723     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46724         config = el;
46725         el = Roo.id();
46726     }
46727     if (config && config.parentLayout) { 
46728         el = config.parentLayout.el.createChild(); 
46729     }
46730     */
46731     if(el.autoCreate){ // xtype is available if this is called from factory
46732         config = el;
46733         el = Roo.id();
46734     }
46735     this.el = Roo.get(el);
46736     if(!this.el && config && config.autoCreate){
46737         if(typeof config.autoCreate == "object"){
46738             if(!config.autoCreate.id){
46739                 config.autoCreate.id = config.id||el;
46740             }
46741             this.el = Roo.DomHelper.append(document.body,
46742                         config.autoCreate, true);
46743         }else{
46744             this.el = Roo.DomHelper.append(document.body,
46745                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46746         }
46747     }
46748     this.closable = false;
46749     this.loaded = false;
46750     this.active = false;
46751     if(typeof config == "string"){
46752         this.title = config;
46753     }else{
46754         Roo.apply(this, config);
46755     }
46756     
46757     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46758         this.wrapEl = this.el.wrap();
46759         this.toolbar.container = this.el.insertSibling(false, 'before');
46760         this.toolbar = new Roo.Toolbar(this.toolbar);
46761     }
46762     
46763     
46764     
46765     if(this.resizeEl){
46766         this.resizeEl = Roo.get(this.resizeEl, true);
46767     }else{
46768         this.resizeEl = this.el;
46769     }
46770     this.addEvents({
46771         /**
46772          * @event activate
46773          * Fires when this panel is activated. 
46774          * @param {Roo.ContentPanel} this
46775          */
46776         "activate" : true,
46777         /**
46778          * @event deactivate
46779          * Fires when this panel is activated. 
46780          * @param {Roo.ContentPanel} this
46781          */
46782         "deactivate" : true,
46783
46784         /**
46785          * @event resize
46786          * Fires when this panel is resized if fitToFrame is true.
46787          * @param {Roo.ContentPanel} this
46788          * @param {Number} width The width after any component adjustments
46789          * @param {Number} height The height after any component adjustments
46790          */
46791         "resize" : true,
46792         
46793          /**
46794          * @event render
46795          * Fires when this tab is created
46796          * @param {Roo.ContentPanel} this
46797          */
46798         "render" : true
46799         
46800         
46801         
46802     });
46803     if(this.autoScroll){
46804         this.resizeEl.setStyle("overflow", "auto");
46805     } else {
46806         // fix randome scrolling
46807         this.el.on('scroll', function() {
46808             Roo.log('fix random scolling');
46809             this.scrollTo('top',0); 
46810         });
46811     }
46812     content = content || this.content;
46813     if(content){
46814         this.setContent(content);
46815     }
46816     if(config && config.url){
46817         this.setUrl(this.url, this.params, this.loadOnce);
46818     }
46819     
46820     
46821     
46822     Roo.ContentPanel.superclass.constructor.call(this);
46823     
46824     this.fireEvent('render', this);
46825 };
46826
46827 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46828     tabTip:'',
46829     setRegion : function(region){
46830         this.region = region;
46831         if(region){
46832            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46833         }else{
46834            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46835         } 
46836     },
46837     
46838     /**
46839      * Returns the toolbar for this Panel if one was configured. 
46840      * @return {Roo.Toolbar} 
46841      */
46842     getToolbar : function(){
46843         return this.toolbar;
46844     },
46845     
46846     setActiveState : function(active){
46847         this.active = active;
46848         if(!active){
46849             this.fireEvent("deactivate", this);
46850         }else{
46851             this.fireEvent("activate", this);
46852         }
46853     },
46854     /**
46855      * Updates this panel's element
46856      * @param {String} content The new content
46857      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46858     */
46859     setContent : function(content, loadScripts){
46860         this.el.update(content, loadScripts);
46861     },
46862
46863     ignoreResize : function(w, h){
46864         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46865             return true;
46866         }else{
46867             this.lastSize = {width: w, height: h};
46868             return false;
46869         }
46870     },
46871     /**
46872      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46873      * @return {Roo.UpdateManager} The UpdateManager
46874      */
46875     getUpdateManager : function(){
46876         return this.el.getUpdateManager();
46877     },
46878      /**
46879      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46880      * @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:
46881 <pre><code>
46882 panel.load({
46883     url: "your-url.php",
46884     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46885     callback: yourFunction,
46886     scope: yourObject, //(optional scope)
46887     discardUrl: false,
46888     nocache: false,
46889     text: "Loading...",
46890     timeout: 30,
46891     scripts: false
46892 });
46893 </code></pre>
46894      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46895      * 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.
46896      * @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}
46897      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46898      * @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.
46899      * @return {Roo.ContentPanel} this
46900      */
46901     load : function(){
46902         var um = this.el.getUpdateManager();
46903         um.update.apply(um, arguments);
46904         return this;
46905     },
46906
46907
46908     /**
46909      * 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.
46910      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46911      * @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)
46912      * @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)
46913      * @return {Roo.UpdateManager} The UpdateManager
46914      */
46915     setUrl : function(url, params, loadOnce){
46916         if(this.refreshDelegate){
46917             this.removeListener("activate", this.refreshDelegate);
46918         }
46919         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46920         this.on("activate", this.refreshDelegate);
46921         return this.el.getUpdateManager();
46922     },
46923     
46924     _handleRefresh : function(url, params, loadOnce){
46925         if(!loadOnce || !this.loaded){
46926             var updater = this.el.getUpdateManager();
46927             updater.update(url, params, this._setLoaded.createDelegate(this));
46928         }
46929     },
46930     
46931     _setLoaded : function(){
46932         this.loaded = true;
46933     }, 
46934     
46935     /**
46936      * Returns this panel's id
46937      * @return {String} 
46938      */
46939     getId : function(){
46940         return this.el.id;
46941     },
46942     
46943     /** 
46944      * Returns this panel's element - used by regiosn to add.
46945      * @return {Roo.Element} 
46946      */
46947     getEl : function(){
46948         return this.wrapEl || this.el;
46949     },
46950     
46951     adjustForComponents : function(width, height){
46952         if(this.resizeEl != this.el){
46953             width -= this.el.getFrameWidth('lr');
46954             height -= this.el.getFrameWidth('tb');
46955         }
46956         if(this.toolbar){
46957             var te = this.toolbar.getEl();
46958             height -= te.getHeight();
46959             te.setWidth(width);
46960         }
46961         if(this.adjustments){
46962             width += this.adjustments[0];
46963             height += this.adjustments[1];
46964         }
46965         return {"width": width, "height": height};
46966     },
46967     
46968     setSize : function(width, height){
46969         if(this.fitToFrame && !this.ignoreResize(width, height)){
46970             if(this.fitContainer && this.resizeEl != this.el){
46971                 this.el.setSize(width, height);
46972             }
46973             var size = this.adjustForComponents(width, height);
46974             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46975             this.fireEvent('resize', this, size.width, size.height);
46976         }
46977     },
46978     
46979     /**
46980      * Returns this panel's title
46981      * @return {String} 
46982      */
46983     getTitle : function(){
46984         return this.title;
46985     },
46986     
46987     /**
46988      * Set this panel's title
46989      * @param {String} title
46990      */
46991     setTitle : function(title){
46992         this.title = title;
46993         if(this.region){
46994             this.region.updatePanelTitle(this, title);
46995         }
46996     },
46997     
46998     /**
46999      * Returns true is this panel was configured to be closable
47000      * @return {Boolean} 
47001      */
47002     isClosable : function(){
47003         return this.closable;
47004     },
47005     
47006     beforeSlide : function(){
47007         this.el.clip();
47008         this.resizeEl.clip();
47009     },
47010     
47011     afterSlide : function(){
47012         this.el.unclip();
47013         this.resizeEl.unclip();
47014     },
47015     
47016     /**
47017      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
47018      *   Will fail silently if the {@link #setUrl} method has not been called.
47019      *   This does not activate the panel, just updates its content.
47020      */
47021     refresh : function(){
47022         if(this.refreshDelegate){
47023            this.loaded = false;
47024            this.refreshDelegate();
47025         }
47026     },
47027     
47028     /**
47029      * Destroys this panel
47030      */
47031     destroy : function(){
47032         this.el.removeAllListeners();
47033         var tempEl = document.createElement("span");
47034         tempEl.appendChild(this.el.dom);
47035         tempEl.innerHTML = "";
47036         this.el.remove();
47037         this.el = null;
47038     },
47039     
47040     /**
47041      * form - if the content panel contains a form - this is a reference to it.
47042      * @type {Roo.form.Form}
47043      */
47044     form : false,
47045     /**
47046      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
47047      *    This contains a reference to it.
47048      * @type {Roo.View}
47049      */
47050     view : false,
47051     
47052       /**
47053      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
47054      * <pre><code>
47055
47056 layout.addxtype({
47057        xtype : 'Form',
47058        items: [ .... ]
47059    }
47060 );
47061
47062 </code></pre>
47063      * @param {Object} cfg Xtype definition of item to add.
47064      */
47065     
47066     addxtype : function(cfg) {
47067         // add form..
47068         if (cfg.xtype.match(/^Form$/)) {
47069             var el = this.el.createChild();
47070
47071             this.form = new  Roo.form.Form(cfg);
47072             
47073             
47074             if ( this.form.allItems.length) this.form.render(el.dom);
47075             return this.form;
47076         }
47077         // should only have one of theses..
47078         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
47079             // views..
47080             cfg.el = this.el.appendChild(document.createElement("div"));
47081             // factory?
47082             
47083             var ret = new Roo.factory(cfg);
47084             ret.render && ret.render(false, ''); // render blank..
47085             this.view = ret;
47086             return ret;
47087         }
47088         return false;
47089     }
47090 });
47091
47092 /**
47093  * @class Roo.GridPanel
47094  * @extends Roo.ContentPanel
47095  * @constructor
47096  * Create a new GridPanel.
47097  * @param {Roo.grid.Grid} grid The grid for this panel
47098  * @param {String/Object} config A string to set only the panel's title, or a config object
47099  */
47100 Roo.GridPanel = function(grid, config){
47101     
47102   
47103     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
47104         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
47105         
47106     this.wrapper.dom.appendChild(grid.getGridEl().dom);
47107     
47108     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
47109     
47110     if(this.toolbar){
47111         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
47112     }
47113     // xtype created footer. - not sure if will work as we normally have to render first..
47114     if (this.footer && !this.footer.el && this.footer.xtype) {
47115         
47116         this.footer.container = this.grid.getView().getFooterPanel(true);
47117         this.footer.dataSource = this.grid.dataSource;
47118         this.footer = Roo.factory(this.footer, Roo);
47119         
47120     }
47121     
47122     grid.monitorWindowResize = false; // turn off autosizing
47123     grid.autoHeight = false;
47124     grid.autoWidth = false;
47125     this.grid = grid;
47126     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
47127 };
47128
47129 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
47130     getId : function(){
47131         return this.grid.id;
47132     },
47133     
47134     /**
47135      * Returns the grid for this panel
47136      * @return {Roo.grid.Grid} 
47137      */
47138     getGrid : function(){
47139         return this.grid;    
47140     },
47141     
47142     setSize : function(width, height){
47143         if(!this.ignoreResize(width, height)){
47144             var grid = this.grid;
47145             var size = this.adjustForComponents(width, height);
47146             grid.getGridEl().setSize(size.width, size.height);
47147             grid.autoSize();
47148         }
47149     },
47150     
47151     beforeSlide : function(){
47152         this.grid.getView().scroller.clip();
47153     },
47154     
47155     afterSlide : function(){
47156         this.grid.getView().scroller.unclip();
47157     },
47158     
47159     destroy : function(){
47160         this.grid.destroy();
47161         delete this.grid;
47162         Roo.GridPanel.superclass.destroy.call(this); 
47163     }
47164 });
47165
47166
47167 /**
47168  * @class Roo.NestedLayoutPanel
47169  * @extends Roo.ContentPanel
47170  * @constructor
47171  * Create a new NestedLayoutPanel.
47172  * 
47173  * 
47174  * @param {Roo.BorderLayout} layout The layout for this panel
47175  * @param {String/Object} config A string to set only the title or a config object
47176  */
47177 Roo.NestedLayoutPanel = function(layout, config)
47178 {
47179     // construct with only one argument..
47180     /* FIXME - implement nicer consturctors
47181     if (layout.layout) {
47182         config = layout;
47183         layout = config.layout;
47184         delete config.layout;
47185     }
47186     if (layout.xtype && !layout.getEl) {
47187         // then layout needs constructing..
47188         layout = Roo.factory(layout, Roo);
47189     }
47190     */
47191     
47192     
47193     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47194     
47195     layout.monitorWindowResize = false; // turn off autosizing
47196     this.layout = layout;
47197     this.layout.getEl().addClass("x-layout-nested-layout");
47198     
47199     
47200     
47201     
47202 };
47203
47204 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47205
47206     setSize : function(width, height){
47207         if(!this.ignoreResize(width, height)){
47208             var size = this.adjustForComponents(width, height);
47209             var el = this.layout.getEl();
47210             el.setSize(size.width, size.height);
47211             var touch = el.dom.offsetWidth;
47212             this.layout.layout();
47213             // ie requires a double layout on the first pass
47214             if(Roo.isIE && !this.initialized){
47215                 this.initialized = true;
47216                 this.layout.layout();
47217             }
47218         }
47219     },
47220     
47221     // activate all subpanels if not currently active..
47222     
47223     setActiveState : function(active){
47224         this.active = active;
47225         if(!active){
47226             this.fireEvent("deactivate", this);
47227             return;
47228         }
47229         
47230         this.fireEvent("activate", this);
47231         // not sure if this should happen before or after..
47232         if (!this.layout) {
47233             return; // should not happen..
47234         }
47235         var reg = false;
47236         for (var r in this.layout.regions) {
47237             reg = this.layout.getRegion(r);
47238             if (reg.getActivePanel()) {
47239                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47240                 reg.setActivePanel(reg.getActivePanel());
47241                 continue;
47242             }
47243             if (!reg.panels.length) {
47244                 continue;
47245             }
47246             reg.showPanel(reg.getPanel(0));
47247         }
47248         
47249         
47250         
47251         
47252     },
47253     
47254     /**
47255      * Returns the nested BorderLayout for this panel
47256      * @return {Roo.BorderLayout} 
47257      */
47258     getLayout : function(){
47259         return this.layout;
47260     },
47261     
47262      /**
47263      * Adds a xtype elements to the layout of the nested panel
47264      * <pre><code>
47265
47266 panel.addxtype({
47267        xtype : 'ContentPanel',
47268        region: 'west',
47269        items: [ .... ]
47270    }
47271 );
47272
47273 panel.addxtype({
47274         xtype : 'NestedLayoutPanel',
47275         region: 'west',
47276         layout: {
47277            center: { },
47278            west: { }   
47279         },
47280         items : [ ... list of content panels or nested layout panels.. ]
47281    }
47282 );
47283 </code></pre>
47284      * @param {Object} cfg Xtype definition of item to add.
47285      */
47286     addxtype : function(cfg) {
47287         return this.layout.addxtype(cfg);
47288     
47289     }
47290 });
47291
47292 Roo.ScrollPanel = function(el, config, content){
47293     config = config || {};
47294     config.fitToFrame = true;
47295     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47296     
47297     this.el.dom.style.overflow = "hidden";
47298     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47299     this.el.removeClass("x-layout-inactive-content");
47300     this.el.on("mousewheel", this.onWheel, this);
47301
47302     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47303     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47304     up.unselectable(); down.unselectable();
47305     up.on("click", this.scrollUp, this);
47306     down.on("click", this.scrollDown, this);
47307     up.addClassOnOver("x-scroller-btn-over");
47308     down.addClassOnOver("x-scroller-btn-over");
47309     up.addClassOnClick("x-scroller-btn-click");
47310     down.addClassOnClick("x-scroller-btn-click");
47311     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47312
47313     this.resizeEl = this.el;
47314     this.el = wrap; this.up = up; this.down = down;
47315 };
47316
47317 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47318     increment : 100,
47319     wheelIncrement : 5,
47320     scrollUp : function(){
47321         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47322     },
47323
47324     scrollDown : function(){
47325         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47326     },
47327
47328     afterScroll : function(){
47329         var el = this.resizeEl;
47330         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47331         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47332         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47333     },
47334
47335     setSize : function(){
47336         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47337         this.afterScroll();
47338     },
47339
47340     onWheel : function(e){
47341         var d = e.getWheelDelta();
47342         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47343         this.afterScroll();
47344         e.stopEvent();
47345     },
47346
47347     setContent : function(content, loadScripts){
47348         this.resizeEl.update(content, loadScripts);
47349     }
47350
47351 });
47352
47353
47354
47355
47356
47357
47358
47359
47360
47361 /**
47362  * @class Roo.TreePanel
47363  * @extends Roo.ContentPanel
47364  * @constructor
47365  * Create a new TreePanel. - defaults to fit/scoll contents.
47366  * @param {String/Object} config A string to set only the panel's title, or a config object
47367  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47368  */
47369 Roo.TreePanel = function(config){
47370     var el = config.el;
47371     var tree = config.tree;
47372     delete config.tree; 
47373     delete config.el; // hopefull!
47374     
47375     // wrapper for IE7 strict & safari scroll issue
47376     
47377     var treeEl = el.createChild();
47378     config.resizeEl = treeEl;
47379     
47380     
47381     
47382     Roo.TreePanel.superclass.constructor.call(this, el, config);
47383  
47384  
47385     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47386     //console.log(tree);
47387     this.on('activate', function()
47388     {
47389         if (this.tree.rendered) {
47390             return;
47391         }
47392         //console.log('render tree');
47393         this.tree.render();
47394     });
47395     
47396     this.on('resize',  function (cp, w, h) {
47397             this.tree.innerCt.setWidth(w);
47398             this.tree.innerCt.setHeight(h);
47399             this.tree.innerCt.setStyle('overflow-y', 'auto');
47400     });
47401
47402         
47403     
47404 };
47405
47406 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47407     fitToFrame : true,
47408     autoScroll : true
47409 });
47410
47411
47412
47413
47414
47415
47416
47417
47418
47419
47420
47421 /*
47422  * Based on:
47423  * Ext JS Library 1.1.1
47424  * Copyright(c) 2006-2007, Ext JS, LLC.
47425  *
47426  * Originally Released Under LGPL - original licence link has changed is not relivant.
47427  *
47428  * Fork - LGPL
47429  * <script type="text/javascript">
47430  */
47431  
47432
47433 /**
47434  * @class Roo.ReaderLayout
47435  * @extends Roo.BorderLayout
47436  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47437  * center region containing two nested regions (a top one for a list view and one for item preview below),
47438  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47439  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47440  * expedites the setup of the overall layout and regions for this common application style.
47441  * Example:
47442  <pre><code>
47443 var reader = new Roo.ReaderLayout();
47444 var CP = Roo.ContentPanel;  // shortcut for adding
47445
47446 reader.beginUpdate();
47447 reader.add("north", new CP("north", "North"));
47448 reader.add("west", new CP("west", {title: "West"}));
47449 reader.add("east", new CP("east", {title: "East"}));
47450
47451 reader.regions.listView.add(new CP("listView", "List"));
47452 reader.regions.preview.add(new CP("preview", "Preview"));
47453 reader.endUpdate();
47454 </code></pre>
47455 * @constructor
47456 * Create a new ReaderLayout
47457 * @param {Object} config Configuration options
47458 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47459 * document.body if omitted)
47460 */
47461 Roo.ReaderLayout = function(config, renderTo){
47462     var c = config || {size:{}};
47463     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47464         north: c.north !== false ? Roo.apply({
47465             split:false,
47466             initialSize: 32,
47467             titlebar: false
47468         }, c.north) : false,
47469         west: c.west !== false ? Roo.apply({
47470             split:true,
47471             initialSize: 200,
47472             minSize: 175,
47473             maxSize: 400,
47474             titlebar: true,
47475             collapsible: true,
47476             animate: true,
47477             margins:{left:5,right:0,bottom:5,top:5},
47478             cmargins:{left:5,right:5,bottom:5,top:5}
47479         }, c.west) : false,
47480         east: c.east !== false ? Roo.apply({
47481             split:true,
47482             initialSize: 200,
47483             minSize: 175,
47484             maxSize: 400,
47485             titlebar: true,
47486             collapsible: true,
47487             animate: true,
47488             margins:{left:0,right:5,bottom:5,top:5},
47489             cmargins:{left:5,right:5,bottom:5,top:5}
47490         }, c.east) : false,
47491         center: Roo.apply({
47492             tabPosition: 'top',
47493             autoScroll:false,
47494             closeOnTab: true,
47495             titlebar:false,
47496             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47497         }, c.center)
47498     });
47499
47500     this.el.addClass('x-reader');
47501
47502     this.beginUpdate();
47503
47504     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47505         south: c.preview !== false ? Roo.apply({
47506             split:true,
47507             initialSize: 200,
47508             minSize: 100,
47509             autoScroll:true,
47510             collapsible:true,
47511             titlebar: true,
47512             cmargins:{top:5,left:0, right:0, bottom:0}
47513         }, c.preview) : false,
47514         center: Roo.apply({
47515             autoScroll:false,
47516             titlebar:false,
47517             minHeight:200
47518         }, c.listView)
47519     });
47520     this.add('center', new Roo.NestedLayoutPanel(inner,
47521             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47522
47523     this.endUpdate();
47524
47525     this.regions.preview = inner.getRegion('south');
47526     this.regions.listView = inner.getRegion('center');
47527 };
47528
47529 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47530  * Based on:
47531  * Ext JS Library 1.1.1
47532  * Copyright(c) 2006-2007, Ext JS, LLC.
47533  *
47534  * Originally Released Under LGPL - original licence link has changed is not relivant.
47535  *
47536  * Fork - LGPL
47537  * <script type="text/javascript">
47538  */
47539  
47540 /**
47541  * @class Roo.grid.Grid
47542  * @extends Roo.util.Observable
47543  * This class represents the primary interface of a component based grid control.
47544  * <br><br>Usage:<pre><code>
47545  var grid = new Roo.grid.Grid("my-container-id", {
47546      ds: myDataStore,
47547      cm: myColModel,
47548      selModel: mySelectionModel,
47549      autoSizeColumns: true,
47550      monitorWindowResize: false,
47551      trackMouseOver: true
47552  });
47553  // set any options
47554  grid.render();
47555  * </code></pre>
47556  * <b>Common Problems:</b><br/>
47557  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47558  * element will correct this<br/>
47559  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47560  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47561  * are unpredictable.<br/>
47562  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47563  * grid to calculate dimensions/offsets.<br/>
47564   * @constructor
47565  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47566  * The container MUST have some type of size defined for the grid to fill. The container will be
47567  * automatically set to position relative if it isn't already.
47568  * @param {Object} config A config object that sets properties on this grid.
47569  */
47570 Roo.grid.Grid = function(container, config){
47571         // initialize the container
47572         this.container = Roo.get(container);
47573         this.container.update("");
47574         this.container.setStyle("overflow", "hidden");
47575     this.container.addClass('x-grid-container');
47576
47577     this.id = this.container.id;
47578
47579     Roo.apply(this, config);
47580     // check and correct shorthanded configs
47581     if(this.ds){
47582         this.dataSource = this.ds;
47583         delete this.ds;
47584     }
47585     if(this.cm){
47586         this.colModel = this.cm;
47587         delete this.cm;
47588     }
47589     if(this.sm){
47590         this.selModel = this.sm;
47591         delete this.sm;
47592     }
47593
47594     if (this.selModel) {
47595         this.selModel = Roo.factory(this.selModel, Roo.grid);
47596         this.sm = this.selModel;
47597         this.sm.xmodule = this.xmodule || false;
47598     }
47599     if (typeof(this.colModel.config) == 'undefined') {
47600         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47601         this.cm = this.colModel;
47602         this.cm.xmodule = this.xmodule || false;
47603     }
47604     if (this.dataSource) {
47605         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47606         this.ds = this.dataSource;
47607         this.ds.xmodule = this.xmodule || false;
47608          
47609     }
47610     
47611     
47612     
47613     if(this.width){
47614         this.container.setWidth(this.width);
47615     }
47616
47617     if(this.height){
47618         this.container.setHeight(this.height);
47619     }
47620     /** @private */
47621         this.addEvents({
47622         // raw events
47623         /**
47624          * @event click
47625          * The raw click event for the entire grid.
47626          * @param {Roo.EventObject} e
47627          */
47628         "click" : true,
47629         /**
47630          * @event dblclick
47631          * The raw dblclick event for the entire grid.
47632          * @param {Roo.EventObject} e
47633          */
47634         "dblclick" : true,
47635         /**
47636          * @event contextmenu
47637          * The raw contextmenu event for the entire grid.
47638          * @param {Roo.EventObject} e
47639          */
47640         "contextmenu" : true,
47641         /**
47642          * @event mousedown
47643          * The raw mousedown event for the entire grid.
47644          * @param {Roo.EventObject} e
47645          */
47646         "mousedown" : true,
47647         /**
47648          * @event mouseup
47649          * The raw mouseup event for the entire grid.
47650          * @param {Roo.EventObject} e
47651          */
47652         "mouseup" : true,
47653         /**
47654          * @event mouseover
47655          * The raw mouseover event for the entire grid.
47656          * @param {Roo.EventObject} e
47657          */
47658         "mouseover" : true,
47659         /**
47660          * @event mouseout
47661          * The raw mouseout event for the entire grid.
47662          * @param {Roo.EventObject} e
47663          */
47664         "mouseout" : true,
47665         /**
47666          * @event keypress
47667          * The raw keypress event for the entire grid.
47668          * @param {Roo.EventObject} e
47669          */
47670         "keypress" : true,
47671         /**
47672          * @event keydown
47673          * The raw keydown event for the entire grid.
47674          * @param {Roo.EventObject} e
47675          */
47676         "keydown" : true,
47677
47678         // custom events
47679
47680         /**
47681          * @event cellclick
47682          * Fires when a cell is clicked
47683          * @param {Grid} this
47684          * @param {Number} rowIndex
47685          * @param {Number} columnIndex
47686          * @param {Roo.EventObject} e
47687          */
47688         "cellclick" : true,
47689         /**
47690          * @event celldblclick
47691          * Fires when a cell is double clicked
47692          * @param {Grid} this
47693          * @param {Number} rowIndex
47694          * @param {Number} columnIndex
47695          * @param {Roo.EventObject} e
47696          */
47697         "celldblclick" : true,
47698         /**
47699          * @event rowclick
47700          * Fires when a row is clicked
47701          * @param {Grid} this
47702          * @param {Number} rowIndex
47703          * @param {Roo.EventObject} e
47704          */
47705         "rowclick" : true,
47706         /**
47707          * @event rowdblclick
47708          * Fires when a row is double clicked
47709          * @param {Grid} this
47710          * @param {Number} rowIndex
47711          * @param {Roo.EventObject} e
47712          */
47713         "rowdblclick" : true,
47714         /**
47715          * @event headerclick
47716          * Fires when a header is clicked
47717          * @param {Grid} this
47718          * @param {Number} columnIndex
47719          * @param {Roo.EventObject} e
47720          */
47721         "headerclick" : true,
47722         /**
47723          * @event headerdblclick
47724          * Fires when a header cell is double clicked
47725          * @param {Grid} this
47726          * @param {Number} columnIndex
47727          * @param {Roo.EventObject} e
47728          */
47729         "headerdblclick" : true,
47730         /**
47731          * @event rowcontextmenu
47732          * Fires when a row is right clicked
47733          * @param {Grid} this
47734          * @param {Number} rowIndex
47735          * @param {Roo.EventObject} e
47736          */
47737         "rowcontextmenu" : true,
47738         /**
47739          * @event cellcontextmenu
47740          * Fires when a cell is right clicked
47741          * @param {Grid} this
47742          * @param {Number} rowIndex
47743          * @param {Number} cellIndex
47744          * @param {Roo.EventObject} e
47745          */
47746          "cellcontextmenu" : true,
47747         /**
47748          * @event headercontextmenu
47749          * Fires when a header is right clicked
47750          * @param {Grid} this
47751          * @param {Number} columnIndex
47752          * @param {Roo.EventObject} e
47753          */
47754         "headercontextmenu" : true,
47755         /**
47756          * @event bodyscroll
47757          * Fires when the body element is scrolled
47758          * @param {Number} scrollLeft
47759          * @param {Number} scrollTop
47760          */
47761         "bodyscroll" : true,
47762         /**
47763          * @event columnresize
47764          * Fires when the user resizes a column
47765          * @param {Number} columnIndex
47766          * @param {Number} newSize
47767          */
47768         "columnresize" : true,
47769         /**
47770          * @event columnmove
47771          * Fires when the user moves a column
47772          * @param {Number} oldIndex
47773          * @param {Number} newIndex
47774          */
47775         "columnmove" : true,
47776         /**
47777          * @event startdrag
47778          * Fires when row(s) start being dragged
47779          * @param {Grid} this
47780          * @param {Roo.GridDD} dd The drag drop object
47781          * @param {event} e The raw browser event
47782          */
47783         "startdrag" : true,
47784         /**
47785          * @event enddrag
47786          * Fires when a drag operation is complete
47787          * @param {Grid} this
47788          * @param {Roo.GridDD} dd The drag drop object
47789          * @param {event} e The raw browser event
47790          */
47791         "enddrag" : true,
47792         /**
47793          * @event dragdrop
47794          * Fires when dragged row(s) are dropped on a valid DD target
47795          * @param {Grid} this
47796          * @param {Roo.GridDD} dd The drag drop object
47797          * @param {String} targetId The target drag drop object
47798          * @param {event} e The raw browser event
47799          */
47800         "dragdrop" : true,
47801         /**
47802          * @event dragover
47803          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47804          * @param {Grid} this
47805          * @param {Roo.GridDD} dd The drag drop object
47806          * @param {String} targetId The target drag drop object
47807          * @param {event} e The raw browser event
47808          */
47809         "dragover" : true,
47810         /**
47811          * @event dragenter
47812          *  Fires when the dragged row(s) first cross another DD target while being dragged
47813          * @param {Grid} this
47814          * @param {Roo.GridDD} dd The drag drop object
47815          * @param {String} targetId The target drag drop object
47816          * @param {event} e The raw browser event
47817          */
47818         "dragenter" : true,
47819         /**
47820          * @event dragout
47821          * Fires when the dragged row(s) leave another DD target while being dragged
47822          * @param {Grid} this
47823          * @param {Roo.GridDD} dd The drag drop object
47824          * @param {String} targetId The target drag drop object
47825          * @param {event} e The raw browser event
47826          */
47827         "dragout" : true,
47828         /**
47829          * @event rowclass
47830          * Fires when a row is rendered, so you can change add a style to it.
47831          * @param {GridView} gridview   The grid view
47832          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47833          */
47834         'rowclass' : true,
47835
47836         /**
47837          * @event render
47838          * Fires when the grid is rendered
47839          * @param {Grid} grid
47840          */
47841         'render' : true
47842     });
47843
47844     Roo.grid.Grid.superclass.constructor.call(this);
47845 };
47846 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47847     
47848     /**
47849      * @cfg {String} ddGroup - drag drop group.
47850      */
47851
47852     /**
47853      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47854      */
47855     minColumnWidth : 25,
47856
47857     /**
47858      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47859      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47860      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47861      */
47862     autoSizeColumns : false,
47863
47864     /**
47865      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47866      */
47867     autoSizeHeaders : true,
47868
47869     /**
47870      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47871      */
47872     monitorWindowResize : true,
47873
47874     /**
47875      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47876      * rows measured to get a columns size. Default is 0 (all rows).
47877      */
47878     maxRowsToMeasure : 0,
47879
47880     /**
47881      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47882      */
47883     trackMouseOver : true,
47884
47885     /**
47886     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47887     */
47888     
47889     /**
47890     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47891     */
47892     enableDragDrop : false,
47893     
47894     /**
47895     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47896     */
47897     enableColumnMove : true,
47898     
47899     /**
47900     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47901     */
47902     enableColumnHide : true,
47903     
47904     /**
47905     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47906     */
47907     enableRowHeightSync : false,
47908     
47909     /**
47910     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47911     */
47912     stripeRows : true,
47913     
47914     /**
47915     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47916     */
47917     autoHeight : false,
47918
47919     /**
47920      * @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.
47921      */
47922     autoExpandColumn : false,
47923
47924     /**
47925     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47926     * Default is 50.
47927     */
47928     autoExpandMin : 50,
47929
47930     /**
47931     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47932     */
47933     autoExpandMax : 1000,
47934
47935     /**
47936     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47937     */
47938     view : null,
47939
47940     /**
47941     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47942     */
47943     loadMask : false,
47944     /**
47945     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47946     */
47947     dropTarget: false,
47948     
47949    
47950     
47951     // private
47952     rendered : false,
47953
47954     /**
47955     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47956     * of a fixed width. Default is false.
47957     */
47958     /**
47959     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47960     */
47961     /**
47962      * Called once after all setup has been completed and the grid is ready to be rendered.
47963      * @return {Roo.grid.Grid} this
47964      */
47965     render : function()
47966     {
47967         var c = this.container;
47968         // try to detect autoHeight/width mode
47969         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47970             this.autoHeight = true;
47971         }
47972         var view = this.getView();
47973         view.init(this);
47974
47975         c.on("click", this.onClick, this);
47976         c.on("dblclick", this.onDblClick, this);
47977         c.on("contextmenu", this.onContextMenu, this);
47978         c.on("keydown", this.onKeyDown, this);
47979
47980         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47981
47982         this.getSelectionModel().init(this);
47983
47984         view.render();
47985
47986         if(this.loadMask){
47987             this.loadMask = new Roo.LoadMask(this.container,
47988                     Roo.apply({store:this.dataSource}, this.loadMask));
47989         }
47990         
47991         
47992         if (this.toolbar && this.toolbar.xtype) {
47993             this.toolbar.container = this.getView().getHeaderPanel(true);
47994             this.toolbar = new Roo.Toolbar(this.toolbar);
47995         }
47996         if (this.footer && this.footer.xtype) {
47997             this.footer.dataSource = this.getDataSource();
47998             this.footer.container = this.getView().getFooterPanel(true);
47999             this.footer = Roo.factory(this.footer, Roo);
48000         }
48001         if (this.dropTarget && this.dropTarget.xtype) {
48002             delete this.dropTarget.xtype;
48003             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
48004         }
48005         
48006         
48007         this.rendered = true;
48008         this.fireEvent('render', this);
48009         return this;
48010     },
48011
48012         /**
48013          * Reconfigures the grid to use a different Store and Column Model.
48014          * The View will be bound to the new objects and refreshed.
48015          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
48016          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
48017          */
48018     reconfigure : function(dataSource, colModel){
48019         if(this.loadMask){
48020             this.loadMask.destroy();
48021             this.loadMask = new Roo.LoadMask(this.container,
48022                     Roo.apply({store:dataSource}, this.loadMask));
48023         }
48024         this.view.bind(dataSource, colModel);
48025         this.dataSource = dataSource;
48026         this.colModel = colModel;
48027         this.view.refresh(true);
48028     },
48029
48030     // private
48031     onKeyDown : function(e){
48032         this.fireEvent("keydown", e);
48033     },
48034
48035     /**
48036      * Destroy this grid.
48037      * @param {Boolean} removeEl True to remove the element
48038      */
48039     destroy : function(removeEl, keepListeners){
48040         if(this.loadMask){
48041             this.loadMask.destroy();
48042         }
48043         var c = this.container;
48044         c.removeAllListeners();
48045         this.view.destroy();
48046         this.colModel.purgeListeners();
48047         if(!keepListeners){
48048             this.purgeListeners();
48049         }
48050         c.update("");
48051         if(removeEl === true){
48052             c.remove();
48053         }
48054     },
48055
48056     // private
48057     processEvent : function(name, e){
48058         this.fireEvent(name, e);
48059         var t = e.getTarget();
48060         var v = this.view;
48061         var header = v.findHeaderIndex(t);
48062         if(header !== false){
48063             this.fireEvent("header" + name, this, header, e);
48064         }else{
48065             var row = v.findRowIndex(t);
48066             var cell = v.findCellIndex(t);
48067             if(row !== false){
48068                 this.fireEvent("row" + name, this, row, e);
48069                 if(cell !== false){
48070                     this.fireEvent("cell" + name, this, row, cell, e);
48071                 }
48072             }
48073         }
48074     },
48075
48076     // private
48077     onClick : function(e){
48078         this.processEvent("click", e);
48079     },
48080
48081     // private
48082     onContextMenu : function(e, t){
48083         this.processEvent("contextmenu", e);
48084     },
48085
48086     // private
48087     onDblClick : function(e){
48088         this.processEvent("dblclick", e);
48089     },
48090
48091     // private
48092     walkCells : function(row, col, step, fn, scope){
48093         var cm = this.colModel, clen = cm.getColumnCount();
48094         var ds = this.dataSource, rlen = ds.getCount(), first = true;
48095         if(step < 0){
48096             if(col < 0){
48097                 row--;
48098                 first = false;
48099             }
48100             while(row >= 0){
48101                 if(!first){
48102                     col = clen-1;
48103                 }
48104                 first = false;
48105                 while(col >= 0){
48106                     if(fn.call(scope || this, row, col, cm) === true){
48107                         return [row, col];
48108                     }
48109                     col--;
48110                 }
48111                 row--;
48112             }
48113         } else {
48114             if(col >= clen){
48115                 row++;
48116                 first = false;
48117             }
48118             while(row < rlen){
48119                 if(!first){
48120                     col = 0;
48121                 }
48122                 first = false;
48123                 while(col < clen){
48124                     if(fn.call(scope || this, row, col, cm) === true){
48125                         return [row, col];
48126                     }
48127                     col++;
48128                 }
48129                 row++;
48130             }
48131         }
48132         return null;
48133     },
48134
48135     // private
48136     getSelections : function(){
48137         return this.selModel.getSelections();
48138     },
48139
48140     /**
48141      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
48142      * but if manual update is required this method will initiate it.
48143      */
48144     autoSize : function(){
48145         if(this.rendered){
48146             this.view.layout();
48147             if(this.view.adjustForScroll){
48148                 this.view.adjustForScroll();
48149             }
48150         }
48151     },
48152
48153     /**
48154      * Returns the grid's underlying element.
48155      * @return {Element} The element
48156      */
48157     getGridEl : function(){
48158         return this.container;
48159     },
48160
48161     // private for compatibility, overridden by editor grid
48162     stopEditing : function(){},
48163
48164     /**
48165      * Returns the grid's SelectionModel.
48166      * @return {SelectionModel}
48167      */
48168     getSelectionModel : function(){
48169         if(!this.selModel){
48170             this.selModel = new Roo.grid.RowSelectionModel();
48171         }
48172         return this.selModel;
48173     },
48174
48175     /**
48176      * Returns the grid's DataSource.
48177      * @return {DataSource}
48178      */
48179     getDataSource : function(){
48180         return this.dataSource;
48181     },
48182
48183     /**
48184      * Returns the grid's ColumnModel.
48185      * @return {ColumnModel}
48186      */
48187     getColumnModel : function(){
48188         return this.colModel;
48189     },
48190
48191     /**
48192      * Returns the grid's GridView object.
48193      * @return {GridView}
48194      */
48195     getView : function(){
48196         if(!this.view){
48197             this.view = new Roo.grid.GridView(this.viewConfig);
48198         }
48199         return this.view;
48200     },
48201     /**
48202      * Called to get grid's drag proxy text, by default returns this.ddText.
48203      * @return {String}
48204      */
48205     getDragDropText : function(){
48206         var count = this.selModel.getCount();
48207         return String.format(this.ddText, count, count == 1 ? '' : 's');
48208     }
48209 });
48210 /**
48211  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48212  * %0 is replaced with the number of selected rows.
48213  * @type String
48214  */
48215 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48216  * Based on:
48217  * Ext JS Library 1.1.1
48218  * Copyright(c) 2006-2007, Ext JS, LLC.
48219  *
48220  * Originally Released Under LGPL - original licence link has changed is not relivant.
48221  *
48222  * Fork - LGPL
48223  * <script type="text/javascript">
48224  */
48225  
48226 Roo.grid.AbstractGridView = function(){
48227         this.grid = null;
48228         
48229         this.events = {
48230             "beforerowremoved" : true,
48231             "beforerowsinserted" : true,
48232             "beforerefresh" : true,
48233             "rowremoved" : true,
48234             "rowsinserted" : true,
48235             "rowupdated" : true,
48236             "refresh" : true
48237         };
48238     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48239 };
48240
48241 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48242     rowClass : "x-grid-row",
48243     cellClass : "x-grid-cell",
48244     tdClass : "x-grid-td",
48245     hdClass : "x-grid-hd",
48246     splitClass : "x-grid-hd-split",
48247     
48248         init: function(grid){
48249         this.grid = grid;
48250                 var cid = this.grid.getGridEl().id;
48251         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48252         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48253         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48254         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48255         },
48256         
48257         getColumnRenderers : function(){
48258         var renderers = [];
48259         var cm = this.grid.colModel;
48260         var colCount = cm.getColumnCount();
48261         for(var i = 0; i < colCount; i++){
48262             renderers[i] = cm.getRenderer(i);
48263         }
48264         return renderers;
48265     },
48266     
48267     getColumnIds : function(){
48268         var ids = [];
48269         var cm = this.grid.colModel;
48270         var colCount = cm.getColumnCount();
48271         for(var i = 0; i < colCount; i++){
48272             ids[i] = cm.getColumnId(i);
48273         }
48274         return ids;
48275     },
48276     
48277     getDataIndexes : function(){
48278         if(!this.indexMap){
48279             this.indexMap = this.buildIndexMap();
48280         }
48281         return this.indexMap.colToData;
48282     },
48283     
48284     getColumnIndexByDataIndex : function(dataIndex){
48285         if(!this.indexMap){
48286             this.indexMap = this.buildIndexMap();
48287         }
48288         return this.indexMap.dataToCol[dataIndex];
48289     },
48290     
48291     /**
48292      * Set a css style for a column dynamically. 
48293      * @param {Number} colIndex The index of the column
48294      * @param {String} name The css property name
48295      * @param {String} value The css value
48296      */
48297     setCSSStyle : function(colIndex, name, value){
48298         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48299         Roo.util.CSS.updateRule(selector, name, value);
48300     },
48301     
48302     generateRules : function(cm){
48303         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48304         Roo.util.CSS.removeStyleSheet(rulesId);
48305         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48306             var cid = cm.getColumnId(i);
48307             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48308                          this.tdSelector, cid, " {\n}\n",
48309                          this.hdSelector, cid, " {\n}\n",
48310                          this.splitSelector, cid, " {\n}\n");
48311         }
48312         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48313     }
48314 });/*
48315  * Based on:
48316  * Ext JS Library 1.1.1
48317  * Copyright(c) 2006-2007, Ext JS, LLC.
48318  *
48319  * Originally Released Under LGPL - original licence link has changed is not relivant.
48320  *
48321  * Fork - LGPL
48322  * <script type="text/javascript">
48323  */
48324
48325 // private
48326 // This is a support class used internally by the Grid components
48327 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48328     this.grid = grid;
48329     this.view = grid.getView();
48330     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48331     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48332     if(hd2){
48333         this.setHandleElId(Roo.id(hd));
48334         this.setOuterHandleElId(Roo.id(hd2));
48335     }
48336     this.scroll = false;
48337 };
48338 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48339     maxDragWidth: 120,
48340     getDragData : function(e){
48341         var t = Roo.lib.Event.getTarget(e);
48342         var h = this.view.findHeaderCell(t);
48343         if(h){
48344             return {ddel: h.firstChild, header:h};
48345         }
48346         return false;
48347     },
48348
48349     onInitDrag : function(e){
48350         this.view.headersDisabled = true;
48351         var clone = this.dragData.ddel.cloneNode(true);
48352         clone.id = Roo.id();
48353         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48354         this.proxy.update(clone);
48355         return true;
48356     },
48357
48358     afterValidDrop : function(){
48359         var v = this.view;
48360         setTimeout(function(){
48361             v.headersDisabled = false;
48362         }, 50);
48363     },
48364
48365     afterInvalidDrop : function(){
48366         var v = this.view;
48367         setTimeout(function(){
48368             v.headersDisabled = false;
48369         }, 50);
48370     }
48371 });
48372 /*
48373  * Based on:
48374  * Ext JS Library 1.1.1
48375  * Copyright(c) 2006-2007, Ext JS, LLC.
48376  *
48377  * Originally Released Under LGPL - original licence link has changed is not relivant.
48378  *
48379  * Fork - LGPL
48380  * <script type="text/javascript">
48381  */
48382 // private
48383 // This is a support class used internally by the Grid components
48384 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48385     this.grid = grid;
48386     this.view = grid.getView();
48387     // split the proxies so they don't interfere with mouse events
48388     this.proxyTop = Roo.DomHelper.append(document.body, {
48389         cls:"col-move-top", html:"&#160;"
48390     }, true);
48391     this.proxyBottom = Roo.DomHelper.append(document.body, {
48392         cls:"col-move-bottom", html:"&#160;"
48393     }, true);
48394     this.proxyTop.hide = this.proxyBottom.hide = function(){
48395         this.setLeftTop(-100,-100);
48396         this.setStyle("visibility", "hidden");
48397     };
48398     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48399     // temporarily disabled
48400     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48401     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48402 };
48403 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48404     proxyOffsets : [-4, -9],
48405     fly: Roo.Element.fly,
48406
48407     getTargetFromEvent : function(e){
48408         var t = Roo.lib.Event.getTarget(e);
48409         var cindex = this.view.findCellIndex(t);
48410         if(cindex !== false){
48411             return this.view.getHeaderCell(cindex);
48412         }
48413         return null;
48414     },
48415
48416     nextVisible : function(h){
48417         var v = this.view, cm = this.grid.colModel;
48418         h = h.nextSibling;
48419         while(h){
48420             if(!cm.isHidden(v.getCellIndex(h))){
48421                 return h;
48422             }
48423             h = h.nextSibling;
48424         }
48425         return null;
48426     },
48427
48428     prevVisible : function(h){
48429         var v = this.view, cm = this.grid.colModel;
48430         h = h.prevSibling;
48431         while(h){
48432             if(!cm.isHidden(v.getCellIndex(h))){
48433                 return h;
48434             }
48435             h = h.prevSibling;
48436         }
48437         return null;
48438     },
48439
48440     positionIndicator : function(h, n, e){
48441         var x = Roo.lib.Event.getPageX(e);
48442         var r = Roo.lib.Dom.getRegion(n.firstChild);
48443         var px, pt, py = r.top + this.proxyOffsets[1];
48444         if((r.right - x) <= (r.right-r.left)/2){
48445             px = r.right+this.view.borderWidth;
48446             pt = "after";
48447         }else{
48448             px = r.left;
48449             pt = "before";
48450         }
48451         var oldIndex = this.view.getCellIndex(h);
48452         var newIndex = this.view.getCellIndex(n);
48453
48454         if(this.grid.colModel.isFixed(newIndex)){
48455             return false;
48456         }
48457
48458         var locked = this.grid.colModel.isLocked(newIndex);
48459
48460         if(pt == "after"){
48461             newIndex++;
48462         }
48463         if(oldIndex < newIndex){
48464             newIndex--;
48465         }
48466         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48467             return false;
48468         }
48469         px +=  this.proxyOffsets[0];
48470         this.proxyTop.setLeftTop(px, py);
48471         this.proxyTop.show();
48472         if(!this.bottomOffset){
48473             this.bottomOffset = this.view.mainHd.getHeight();
48474         }
48475         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48476         this.proxyBottom.show();
48477         return pt;
48478     },
48479
48480     onNodeEnter : function(n, dd, e, data){
48481         if(data.header != n){
48482             this.positionIndicator(data.header, n, e);
48483         }
48484     },
48485
48486     onNodeOver : function(n, dd, e, data){
48487         var result = false;
48488         if(data.header != n){
48489             result = this.positionIndicator(data.header, n, e);
48490         }
48491         if(!result){
48492             this.proxyTop.hide();
48493             this.proxyBottom.hide();
48494         }
48495         return result ? this.dropAllowed : this.dropNotAllowed;
48496     },
48497
48498     onNodeOut : function(n, dd, e, data){
48499         this.proxyTop.hide();
48500         this.proxyBottom.hide();
48501     },
48502
48503     onNodeDrop : function(n, dd, e, data){
48504         var h = data.header;
48505         if(h != n){
48506             var cm = this.grid.colModel;
48507             var x = Roo.lib.Event.getPageX(e);
48508             var r = Roo.lib.Dom.getRegion(n.firstChild);
48509             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48510             var oldIndex = this.view.getCellIndex(h);
48511             var newIndex = this.view.getCellIndex(n);
48512             var locked = cm.isLocked(newIndex);
48513             if(pt == "after"){
48514                 newIndex++;
48515             }
48516             if(oldIndex < newIndex){
48517                 newIndex--;
48518             }
48519             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48520                 return false;
48521             }
48522             cm.setLocked(oldIndex, locked, true);
48523             cm.moveColumn(oldIndex, newIndex);
48524             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48525             return true;
48526         }
48527         return false;
48528     }
48529 });
48530 /*
48531  * Based on:
48532  * Ext JS Library 1.1.1
48533  * Copyright(c) 2006-2007, Ext JS, LLC.
48534  *
48535  * Originally Released Under LGPL - original licence link has changed is not relivant.
48536  *
48537  * Fork - LGPL
48538  * <script type="text/javascript">
48539  */
48540   
48541 /**
48542  * @class Roo.grid.GridView
48543  * @extends Roo.util.Observable
48544  *
48545  * @constructor
48546  * @param {Object} config
48547  */
48548 Roo.grid.GridView = function(config){
48549     Roo.grid.GridView.superclass.constructor.call(this);
48550     this.el = null;
48551
48552     Roo.apply(this, config);
48553 };
48554
48555 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48556
48557     
48558     rowClass : "x-grid-row",
48559
48560     cellClass : "x-grid-col",
48561
48562     tdClass : "x-grid-td",
48563
48564     hdClass : "x-grid-hd",
48565
48566     splitClass : "x-grid-split",
48567
48568     sortClasses : ["sort-asc", "sort-desc"],
48569
48570     enableMoveAnim : false,
48571
48572     hlColor: "C3DAF9",
48573
48574     dh : Roo.DomHelper,
48575
48576     fly : Roo.Element.fly,
48577
48578     css : Roo.util.CSS,
48579
48580     borderWidth: 1,
48581
48582     splitOffset: 3,
48583
48584     scrollIncrement : 22,
48585
48586     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48587
48588     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48589
48590     bind : function(ds, cm){
48591         if(this.ds){
48592             this.ds.un("load", this.onLoad, this);
48593             this.ds.un("datachanged", this.onDataChange, this);
48594             this.ds.un("add", this.onAdd, this);
48595             this.ds.un("remove", this.onRemove, this);
48596             this.ds.un("update", this.onUpdate, this);
48597             this.ds.un("clear", this.onClear, this);
48598         }
48599         if(ds){
48600             ds.on("load", this.onLoad, this);
48601             ds.on("datachanged", this.onDataChange, this);
48602             ds.on("add", this.onAdd, this);
48603             ds.on("remove", this.onRemove, this);
48604             ds.on("update", this.onUpdate, this);
48605             ds.on("clear", this.onClear, this);
48606         }
48607         this.ds = ds;
48608
48609         if(this.cm){
48610             this.cm.un("widthchange", this.onColWidthChange, this);
48611             this.cm.un("headerchange", this.onHeaderChange, this);
48612             this.cm.un("hiddenchange", this.onHiddenChange, this);
48613             this.cm.un("columnmoved", this.onColumnMove, this);
48614             this.cm.un("columnlockchange", this.onColumnLock, this);
48615         }
48616         if(cm){
48617             this.generateRules(cm);
48618             cm.on("widthchange", this.onColWidthChange, this);
48619             cm.on("headerchange", this.onHeaderChange, this);
48620             cm.on("hiddenchange", this.onHiddenChange, this);
48621             cm.on("columnmoved", this.onColumnMove, this);
48622             cm.on("columnlockchange", this.onColumnLock, this);
48623         }
48624         this.cm = cm;
48625     },
48626
48627     init: function(grid){
48628         Roo.grid.GridView.superclass.init.call(this, grid);
48629
48630         this.bind(grid.dataSource, grid.colModel);
48631
48632         grid.on("headerclick", this.handleHeaderClick, this);
48633
48634         if(grid.trackMouseOver){
48635             grid.on("mouseover", this.onRowOver, this);
48636             grid.on("mouseout", this.onRowOut, this);
48637         }
48638         grid.cancelTextSelection = function(){};
48639         this.gridId = grid.id;
48640
48641         var tpls = this.templates || {};
48642
48643         if(!tpls.master){
48644             tpls.master = new Roo.Template(
48645                '<div class="x-grid" hidefocus="true">',
48646                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48647                   '<div class="x-grid-topbar"></div>',
48648                   '<div class="x-grid-scroller"><div></div></div>',
48649                   '<div class="x-grid-locked">',
48650                       '<div class="x-grid-header">{lockedHeader}</div>',
48651                       '<div class="x-grid-body">{lockedBody}</div>',
48652                   "</div>",
48653                   '<div class="x-grid-viewport">',
48654                       '<div class="x-grid-header">{header}</div>',
48655                       '<div class="x-grid-body">{body}</div>',
48656                   "</div>",
48657                   '<div class="x-grid-bottombar"></div>',
48658                  
48659                   '<div class="x-grid-resize-proxy">&#160;</div>',
48660                "</div>"
48661             );
48662             tpls.master.disableformats = true;
48663         }
48664
48665         if(!tpls.header){
48666             tpls.header = new Roo.Template(
48667                '<table border="0" cellspacing="0" cellpadding="0">',
48668                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48669                "</table>{splits}"
48670             );
48671             tpls.header.disableformats = true;
48672         }
48673         tpls.header.compile();
48674
48675         if(!tpls.hcell){
48676             tpls.hcell = new Roo.Template(
48677                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48678                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48679                 "</div></td>"
48680              );
48681              tpls.hcell.disableFormats = true;
48682         }
48683         tpls.hcell.compile();
48684
48685         if(!tpls.hsplit){
48686             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48687             tpls.hsplit.disableFormats = true;
48688         }
48689         tpls.hsplit.compile();
48690
48691         if(!tpls.body){
48692             tpls.body = new Roo.Template(
48693                '<table border="0" cellspacing="0" cellpadding="0">',
48694                "<tbody>{rows}</tbody>",
48695                "</table>"
48696             );
48697             tpls.body.disableFormats = true;
48698         }
48699         tpls.body.compile();
48700
48701         if(!tpls.row){
48702             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48703             tpls.row.disableFormats = true;
48704         }
48705         tpls.row.compile();
48706
48707         if(!tpls.cell){
48708             tpls.cell = new Roo.Template(
48709                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48710                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48711                 "</td>"
48712             );
48713             tpls.cell.disableFormats = true;
48714         }
48715         tpls.cell.compile();
48716
48717         this.templates = tpls;
48718     },
48719
48720     // remap these for backwards compat
48721     onColWidthChange : function(){
48722         this.updateColumns.apply(this, arguments);
48723     },
48724     onHeaderChange : function(){
48725         this.updateHeaders.apply(this, arguments);
48726     }, 
48727     onHiddenChange : function(){
48728         this.handleHiddenChange.apply(this, arguments);
48729     },
48730     onColumnMove : function(){
48731         this.handleColumnMove.apply(this, arguments);
48732     },
48733     onColumnLock : function(){
48734         this.handleLockChange.apply(this, arguments);
48735     },
48736
48737     onDataChange : function(){
48738         this.refresh();
48739         this.updateHeaderSortState();
48740     },
48741
48742     onClear : function(){
48743         this.refresh();
48744     },
48745
48746     onUpdate : function(ds, record){
48747         this.refreshRow(record);
48748     },
48749
48750     refreshRow : function(record){
48751         var ds = this.ds, index;
48752         if(typeof record == 'number'){
48753             index = record;
48754             record = ds.getAt(index);
48755         }else{
48756             index = ds.indexOf(record);
48757         }
48758         this.insertRows(ds, index, index, true);
48759         this.onRemove(ds, record, index+1, true);
48760         this.syncRowHeights(index, index);
48761         this.layout();
48762         this.fireEvent("rowupdated", this, index, record);
48763     },
48764
48765     onAdd : function(ds, records, index){
48766         this.insertRows(ds, index, index + (records.length-1));
48767     },
48768
48769     onRemove : function(ds, record, index, isUpdate){
48770         if(isUpdate !== true){
48771             this.fireEvent("beforerowremoved", this, index, record);
48772         }
48773         var bt = this.getBodyTable(), lt = this.getLockedTable();
48774         if(bt.rows[index]){
48775             bt.firstChild.removeChild(bt.rows[index]);
48776         }
48777         if(lt.rows[index]){
48778             lt.firstChild.removeChild(lt.rows[index]);
48779         }
48780         if(isUpdate !== true){
48781             this.stripeRows(index);
48782             this.syncRowHeights(index, index);
48783             this.layout();
48784             this.fireEvent("rowremoved", this, index, record);
48785         }
48786     },
48787
48788     onLoad : function(){
48789         this.scrollToTop();
48790     },
48791
48792     /**
48793      * Scrolls the grid to the top
48794      */
48795     scrollToTop : function(){
48796         if(this.scroller){
48797             this.scroller.dom.scrollTop = 0;
48798             this.syncScroll();
48799         }
48800     },
48801
48802     /**
48803      * Gets a panel in the header of the grid that can be used for toolbars etc.
48804      * After modifying the contents of this panel a call to grid.autoSize() may be
48805      * required to register any changes in size.
48806      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48807      * @return Roo.Element
48808      */
48809     getHeaderPanel : function(doShow){
48810         if(doShow){
48811             this.headerPanel.show();
48812         }
48813         return this.headerPanel;
48814     },
48815
48816     /**
48817      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48818      * After modifying the contents of this panel a call to grid.autoSize() may be
48819      * required to register any changes in size.
48820      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48821      * @return Roo.Element
48822      */
48823     getFooterPanel : function(doShow){
48824         if(doShow){
48825             this.footerPanel.show();
48826         }
48827         return this.footerPanel;
48828     },
48829
48830     initElements : function(){
48831         var E = Roo.Element;
48832         var el = this.grid.getGridEl().dom.firstChild;
48833         var cs = el.childNodes;
48834
48835         this.el = new E(el);
48836         
48837          this.focusEl = new E(el.firstChild);
48838         this.focusEl.swallowEvent("click", true);
48839         
48840         this.headerPanel = new E(cs[1]);
48841         this.headerPanel.enableDisplayMode("block");
48842
48843         this.scroller = new E(cs[2]);
48844         this.scrollSizer = new E(this.scroller.dom.firstChild);
48845
48846         this.lockedWrap = new E(cs[3]);
48847         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48848         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48849
48850         this.mainWrap = new E(cs[4]);
48851         this.mainHd = new E(this.mainWrap.dom.firstChild);
48852         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48853
48854         this.footerPanel = new E(cs[5]);
48855         this.footerPanel.enableDisplayMode("block");
48856
48857         this.resizeProxy = new E(cs[6]);
48858
48859         this.headerSelector = String.format(
48860            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48861            this.lockedHd.id, this.mainHd.id
48862         );
48863
48864         this.splitterSelector = String.format(
48865            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48866            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48867         );
48868     },
48869     idToCssName : function(s)
48870     {
48871         return s.replace(/[^a-z0-9]+/ig, '-');
48872     },
48873
48874     getHeaderCell : function(index){
48875         return Roo.DomQuery.select(this.headerSelector)[index];
48876     },
48877
48878     getHeaderCellMeasure : function(index){
48879         return this.getHeaderCell(index).firstChild;
48880     },
48881
48882     getHeaderCellText : function(index){
48883         return this.getHeaderCell(index).firstChild.firstChild;
48884     },
48885
48886     getLockedTable : function(){
48887         return this.lockedBody.dom.firstChild;
48888     },
48889
48890     getBodyTable : function(){
48891         return this.mainBody.dom.firstChild;
48892     },
48893
48894     getLockedRow : function(index){
48895         return this.getLockedTable().rows[index];
48896     },
48897
48898     getRow : function(index){
48899         return this.getBodyTable().rows[index];
48900     },
48901
48902     getRowComposite : function(index){
48903         if(!this.rowEl){
48904             this.rowEl = new Roo.CompositeElementLite();
48905         }
48906         var els = [], lrow, mrow;
48907         if(lrow = this.getLockedRow(index)){
48908             els.push(lrow);
48909         }
48910         if(mrow = this.getRow(index)){
48911             els.push(mrow);
48912         }
48913         this.rowEl.elements = els;
48914         return this.rowEl;
48915     },
48916     /**
48917      * Gets the 'td' of the cell
48918      * 
48919      * @param {Integer} rowIndex row to select
48920      * @param {Integer} colIndex column to select
48921      * 
48922      * @return {Object} 
48923      */
48924     getCell : function(rowIndex, colIndex){
48925         var locked = this.cm.getLockedCount();
48926         var source;
48927         if(colIndex < locked){
48928             source = this.lockedBody.dom.firstChild;
48929         }else{
48930             source = this.mainBody.dom.firstChild;
48931             colIndex -= locked;
48932         }
48933         return source.rows[rowIndex].childNodes[colIndex];
48934     },
48935
48936     getCellText : function(rowIndex, colIndex){
48937         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48938     },
48939
48940     getCellBox : function(cell){
48941         var b = this.fly(cell).getBox();
48942         if(Roo.isOpera){ // opera fails to report the Y
48943             b.y = cell.offsetTop + this.mainBody.getY();
48944         }
48945         return b;
48946     },
48947
48948     getCellIndex : function(cell){
48949         var id = String(cell.className).match(this.cellRE);
48950         if(id){
48951             return parseInt(id[1], 10);
48952         }
48953         return 0;
48954     },
48955
48956     findHeaderIndex : function(n){
48957         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48958         return r ? this.getCellIndex(r) : false;
48959     },
48960
48961     findHeaderCell : function(n){
48962         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48963         return r ? r : false;
48964     },
48965
48966     findRowIndex : function(n){
48967         if(!n){
48968             return false;
48969         }
48970         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48971         return r ? r.rowIndex : false;
48972     },
48973
48974     findCellIndex : function(node){
48975         var stop = this.el.dom;
48976         while(node && node != stop){
48977             if(this.findRE.test(node.className)){
48978                 return this.getCellIndex(node);
48979             }
48980             node = node.parentNode;
48981         }
48982         return false;
48983     },
48984
48985     getColumnId : function(index){
48986         return this.cm.getColumnId(index);
48987     },
48988
48989     getSplitters : function()
48990     {
48991         if(this.splitterSelector){
48992            return Roo.DomQuery.select(this.splitterSelector);
48993         }else{
48994             return null;
48995       }
48996     },
48997
48998     getSplitter : function(index){
48999         return this.getSplitters()[index];
49000     },
49001
49002     onRowOver : function(e, t){
49003         var row;
49004         if((row = this.findRowIndex(t)) !== false){
49005             this.getRowComposite(row).addClass("x-grid-row-over");
49006         }
49007     },
49008
49009     onRowOut : function(e, t){
49010         var row;
49011         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
49012             this.getRowComposite(row).removeClass("x-grid-row-over");
49013         }
49014     },
49015
49016     renderHeaders : function(){
49017         var cm = this.cm;
49018         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
49019         var cb = [], lb = [], sb = [], lsb = [], p = {};
49020         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49021             p.cellId = "x-grid-hd-0-" + i;
49022             p.splitId = "x-grid-csplit-0-" + i;
49023             p.id = cm.getColumnId(i);
49024             p.title = cm.getColumnTooltip(i) || "";
49025             p.value = cm.getColumnHeader(i) || "";
49026             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
49027             if(!cm.isLocked(i)){
49028                 cb[cb.length] = ct.apply(p);
49029                 sb[sb.length] = st.apply(p);
49030             }else{
49031                 lb[lb.length] = ct.apply(p);
49032                 lsb[lsb.length] = st.apply(p);
49033             }
49034         }
49035         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
49036                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
49037     },
49038
49039     updateHeaders : function(){
49040         var html = this.renderHeaders();
49041         this.lockedHd.update(html[0]);
49042         this.mainHd.update(html[1]);
49043     },
49044
49045     /**
49046      * Focuses the specified row.
49047      * @param {Number} row The row index
49048      */
49049     focusRow : function(row)
49050     {
49051         //Roo.log('GridView.focusRow');
49052         var x = this.scroller.dom.scrollLeft;
49053         this.focusCell(row, 0, false);
49054         this.scroller.dom.scrollLeft = x;
49055     },
49056
49057     /**
49058      * Focuses the specified cell.
49059      * @param {Number} row The row index
49060      * @param {Number} col The column index
49061      * @param {Boolean} hscroll false to disable horizontal scrolling
49062      */
49063     focusCell : function(row, col, hscroll)
49064     {
49065         //Roo.log('GridView.focusCell');
49066         var el = this.ensureVisible(row, col, hscroll);
49067         this.focusEl.alignTo(el, "tl-tl");
49068         if(Roo.isGecko){
49069             this.focusEl.focus();
49070         }else{
49071             this.focusEl.focus.defer(1, this.focusEl);
49072         }
49073     },
49074
49075     /**
49076      * Scrolls the specified cell into view
49077      * @param {Number} row The row index
49078      * @param {Number} col The column index
49079      * @param {Boolean} hscroll false to disable horizontal scrolling
49080      */
49081     ensureVisible : function(row, col, hscroll)
49082     {
49083         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
49084         //return null; //disable for testing.
49085         if(typeof row != "number"){
49086             row = row.rowIndex;
49087         }
49088         if(row < 0 && row >= this.ds.getCount()){
49089             return  null;
49090         }
49091         col = (col !== undefined ? col : 0);
49092         var cm = this.grid.colModel;
49093         while(cm.isHidden(col)){
49094             col++;
49095         }
49096
49097         var el = this.getCell(row, col);
49098         if(!el){
49099             return null;
49100         }
49101         var c = this.scroller.dom;
49102
49103         var ctop = parseInt(el.offsetTop, 10);
49104         var cleft = parseInt(el.offsetLeft, 10);
49105         var cbot = ctop + el.offsetHeight;
49106         var cright = cleft + el.offsetWidth;
49107         
49108         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
49109         var stop = parseInt(c.scrollTop, 10);
49110         var sleft = parseInt(c.scrollLeft, 10);
49111         var sbot = stop + ch;
49112         var sright = sleft + c.clientWidth;
49113         /*
49114         Roo.log('GridView.ensureVisible:' +
49115                 ' ctop:' + ctop +
49116                 ' c.clientHeight:' + c.clientHeight +
49117                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
49118                 ' stop:' + stop +
49119                 ' cbot:' + cbot +
49120                 ' sbot:' + sbot +
49121                 ' ch:' + ch  
49122                 );
49123         */
49124         if(ctop < stop){
49125              c.scrollTop = ctop;
49126             //Roo.log("set scrolltop to ctop DISABLE?");
49127         }else if(cbot > sbot){
49128             //Roo.log("set scrolltop to cbot-ch");
49129             c.scrollTop = cbot-ch;
49130         }
49131         
49132         if(hscroll !== false){
49133             if(cleft < sleft){
49134                 c.scrollLeft = cleft;
49135             }else if(cright > sright){
49136                 c.scrollLeft = cright-c.clientWidth;
49137             }
49138         }
49139          
49140         return el;
49141     },
49142
49143     updateColumns : function(){
49144         this.grid.stopEditing();
49145         var cm = this.grid.colModel, colIds = this.getColumnIds();
49146         //var totalWidth = cm.getTotalWidth();
49147         var pos = 0;
49148         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49149             //if(cm.isHidden(i)) continue;
49150             var w = cm.getColumnWidth(i);
49151             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49152             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49153         }
49154         this.updateSplitters();
49155     },
49156
49157     generateRules : function(cm){
49158         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
49159         Roo.util.CSS.removeStyleSheet(rulesId);
49160         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49161             var cid = cm.getColumnId(i);
49162             var align = '';
49163             if(cm.config[i].align){
49164                 align = 'text-align:'+cm.config[i].align+';';
49165             }
49166             var hidden = '';
49167             if(cm.isHidden(i)){
49168                 hidden = 'display:none;';
49169             }
49170             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49171             ruleBuf.push(
49172                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49173                     this.hdSelector, cid, " {\n", align, width, "}\n",
49174                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49175                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49176         }
49177         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49178     },
49179
49180     updateSplitters : function(){
49181         var cm = this.cm, s = this.getSplitters();
49182         if(s){ // splitters not created yet
49183             var pos = 0, locked = true;
49184             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49185                 if(cm.isHidden(i)) continue;
49186                 var w = cm.getColumnWidth(i); // make sure it's a number
49187                 if(!cm.isLocked(i) && locked){
49188                     pos = 0;
49189                     locked = false;
49190                 }
49191                 pos += w;
49192                 s[i].style.left = (pos-this.splitOffset) + "px";
49193             }
49194         }
49195     },
49196
49197     handleHiddenChange : function(colModel, colIndex, hidden){
49198         if(hidden){
49199             this.hideColumn(colIndex);
49200         }else{
49201             this.unhideColumn(colIndex);
49202         }
49203     },
49204
49205     hideColumn : function(colIndex){
49206         var cid = this.getColumnId(colIndex);
49207         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49208         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49209         if(Roo.isSafari){
49210             this.updateHeaders();
49211         }
49212         this.updateSplitters();
49213         this.layout();
49214     },
49215
49216     unhideColumn : function(colIndex){
49217         var cid = this.getColumnId(colIndex);
49218         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49219         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49220
49221         if(Roo.isSafari){
49222             this.updateHeaders();
49223         }
49224         this.updateSplitters();
49225         this.layout();
49226     },
49227
49228     insertRows : function(dm, firstRow, lastRow, isUpdate){
49229         if(firstRow == 0 && lastRow == dm.getCount()-1){
49230             this.refresh();
49231         }else{
49232             if(!isUpdate){
49233                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49234             }
49235             var s = this.getScrollState();
49236             var markup = this.renderRows(firstRow, lastRow);
49237             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49238             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49239             this.restoreScroll(s);
49240             if(!isUpdate){
49241                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49242                 this.syncRowHeights(firstRow, lastRow);
49243                 this.stripeRows(firstRow);
49244                 this.layout();
49245             }
49246         }
49247     },
49248
49249     bufferRows : function(markup, target, index){
49250         var before = null, trows = target.rows, tbody = target.tBodies[0];
49251         if(index < trows.length){
49252             before = trows[index];
49253         }
49254         var b = document.createElement("div");
49255         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49256         var rows = b.firstChild.rows;
49257         for(var i = 0, len = rows.length; i < len; i++){
49258             if(before){
49259                 tbody.insertBefore(rows[0], before);
49260             }else{
49261                 tbody.appendChild(rows[0]);
49262             }
49263         }
49264         b.innerHTML = "";
49265         b = null;
49266     },
49267
49268     deleteRows : function(dm, firstRow, lastRow){
49269         if(dm.getRowCount()<1){
49270             this.fireEvent("beforerefresh", this);
49271             this.mainBody.update("");
49272             this.lockedBody.update("");
49273             this.fireEvent("refresh", this);
49274         }else{
49275             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49276             var bt = this.getBodyTable();
49277             var tbody = bt.firstChild;
49278             var rows = bt.rows;
49279             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49280                 tbody.removeChild(rows[firstRow]);
49281             }
49282             this.stripeRows(firstRow);
49283             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49284         }
49285     },
49286
49287     updateRows : function(dataSource, firstRow, lastRow){
49288         var s = this.getScrollState();
49289         this.refresh();
49290         this.restoreScroll(s);
49291     },
49292
49293     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49294         if(!noRefresh){
49295            this.refresh();
49296         }
49297         this.updateHeaderSortState();
49298     },
49299
49300     getScrollState : function(){
49301         
49302         var sb = this.scroller.dom;
49303         return {left: sb.scrollLeft, top: sb.scrollTop};
49304     },
49305
49306     stripeRows : function(startRow){
49307         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49308             return;
49309         }
49310         startRow = startRow || 0;
49311         var rows = this.getBodyTable().rows;
49312         var lrows = this.getLockedTable().rows;
49313         var cls = ' x-grid-row-alt ';
49314         for(var i = startRow, len = rows.length; i < len; i++){
49315             var row = rows[i], lrow = lrows[i];
49316             var isAlt = ((i+1) % 2 == 0);
49317             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49318             if(isAlt == hasAlt){
49319                 continue;
49320             }
49321             if(isAlt){
49322                 row.className += " x-grid-row-alt";
49323             }else{
49324                 row.className = row.className.replace("x-grid-row-alt", "");
49325             }
49326             if(lrow){
49327                 lrow.className = row.className;
49328             }
49329         }
49330     },
49331
49332     restoreScroll : function(state){
49333         //Roo.log('GridView.restoreScroll');
49334         var sb = this.scroller.dom;
49335         sb.scrollLeft = state.left;
49336         sb.scrollTop = state.top;
49337         this.syncScroll();
49338     },
49339
49340     syncScroll : function(){
49341         //Roo.log('GridView.syncScroll');
49342         var sb = this.scroller.dom;
49343         var sh = this.mainHd.dom;
49344         var bs = this.mainBody.dom;
49345         var lv = this.lockedBody.dom;
49346         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49347         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49348     },
49349
49350     handleScroll : function(e){
49351         this.syncScroll();
49352         var sb = this.scroller.dom;
49353         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49354         e.stopEvent();
49355     },
49356
49357     handleWheel : function(e){
49358         var d = e.getWheelDelta();
49359         this.scroller.dom.scrollTop -= d*22;
49360         // set this here to prevent jumpy scrolling on large tables
49361         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49362         e.stopEvent();
49363     },
49364
49365     renderRows : function(startRow, endRow){
49366         // pull in all the crap needed to render rows
49367         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49368         var colCount = cm.getColumnCount();
49369
49370         if(ds.getCount() < 1){
49371             return ["", ""];
49372         }
49373
49374         // build a map for all the columns
49375         var cs = [];
49376         for(var i = 0; i < colCount; i++){
49377             var name = cm.getDataIndex(i);
49378             cs[i] = {
49379                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49380                 renderer : cm.getRenderer(i),
49381                 id : cm.getColumnId(i),
49382                 locked : cm.isLocked(i)
49383             };
49384         }
49385
49386         startRow = startRow || 0;
49387         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49388
49389         // records to render
49390         var rs = ds.getRange(startRow, endRow);
49391
49392         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49393     },
49394
49395     // As much as I hate to duplicate code, this was branched because FireFox really hates
49396     // [].join("") on strings. The performance difference was substantial enough to
49397     // branch this function
49398     doRender : Roo.isGecko ?
49399             function(cs, rs, ds, startRow, colCount, stripe){
49400                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49401                 // buffers
49402                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49403                 
49404                 var hasListener = this.grid.hasListener('rowclass');
49405                 var rowcfg = {};
49406                 for(var j = 0, len = rs.length; j < len; j++){
49407                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49408                     for(var i = 0; i < colCount; i++){
49409                         c = cs[i];
49410                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49411                         p.id = c.id;
49412                         p.css = p.attr = "";
49413                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49414                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49415                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49416                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49417                         }
49418                         var markup = ct.apply(p);
49419                         if(!c.locked){
49420                             cb+= markup;
49421                         }else{
49422                             lcb+= markup;
49423                         }
49424                     }
49425                     var alt = [];
49426                     if(stripe && ((rowIndex+1) % 2 == 0)){
49427                         alt.push("x-grid-row-alt")
49428                     }
49429                     if(r.dirty){
49430                         alt.push(  " x-grid-dirty-row");
49431                     }
49432                     rp.cells = lcb;
49433                     if(this.getRowClass){
49434                         alt.push(this.getRowClass(r, rowIndex));
49435                     }
49436                     if (hasListener) {
49437                         rowcfg = {
49438                              
49439                             record: r,
49440                             rowIndex : rowIndex,
49441                             rowClass : ''
49442                         }
49443                         this.grid.fireEvent('rowclass', this, rowcfg);
49444                         alt.push(rowcfg.rowClass);
49445                     }
49446                     rp.alt = alt.join(" ");
49447                     lbuf+= rt.apply(rp);
49448                     rp.cells = cb;
49449                     buf+=  rt.apply(rp);
49450                 }
49451                 return [lbuf, buf];
49452             } :
49453             function(cs, rs, ds, startRow, colCount, stripe){
49454                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49455                 // buffers
49456                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49457                 var hasListener = this.grid.hasListener('rowclass');
49458  
49459                 var rowcfg = {};
49460                 for(var j = 0, len = rs.length; j < len; j++){
49461                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49462                     for(var i = 0; i < colCount; i++){
49463                         c = cs[i];
49464                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49465                         p.id = c.id;
49466                         p.css = p.attr = "";
49467                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49468                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49469                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49470                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49471                         }
49472                         
49473                         var markup = ct.apply(p);
49474                         if(!c.locked){
49475                             cb[cb.length] = markup;
49476                         }else{
49477                             lcb[lcb.length] = markup;
49478                         }
49479                     }
49480                     var alt = [];
49481                     if(stripe && ((rowIndex+1) % 2 == 0)){
49482                         alt.push( "x-grid-row-alt");
49483                     }
49484                     if(r.dirty){
49485                         alt.push(" x-grid-dirty-row");
49486                     }
49487                     rp.cells = lcb;
49488                     if(this.getRowClass){
49489                         alt.push( this.getRowClass(r, rowIndex));
49490                     }
49491                     if (hasListener) {
49492                         rowcfg = {
49493                              
49494                             record: r,
49495                             rowIndex : rowIndex,
49496                             rowClass : ''
49497                         }
49498                         this.grid.fireEvent('rowclass', this, rowcfg);
49499                         alt.push(rowcfg.rowClass);
49500                     }
49501                     rp.alt = alt.join(" ");
49502                     rp.cells = lcb.join("");
49503                     lbuf[lbuf.length] = rt.apply(rp);
49504                     rp.cells = cb.join("");
49505                     buf[buf.length] =  rt.apply(rp);
49506                 }
49507                 return [lbuf.join(""), buf.join("")];
49508             },
49509
49510     renderBody : function(){
49511         var markup = this.renderRows();
49512         var bt = this.templates.body;
49513         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49514     },
49515
49516     /**
49517      * Refreshes the grid
49518      * @param {Boolean} headersToo
49519      */
49520     refresh : function(headersToo){
49521         this.fireEvent("beforerefresh", this);
49522         this.grid.stopEditing();
49523         var result = this.renderBody();
49524         this.lockedBody.update(result[0]);
49525         this.mainBody.update(result[1]);
49526         if(headersToo === true){
49527             this.updateHeaders();
49528             this.updateColumns();
49529             this.updateSplitters();
49530             this.updateHeaderSortState();
49531         }
49532         this.syncRowHeights();
49533         this.layout();
49534         this.fireEvent("refresh", this);
49535     },
49536
49537     handleColumnMove : function(cm, oldIndex, newIndex){
49538         this.indexMap = null;
49539         var s = this.getScrollState();
49540         this.refresh(true);
49541         this.restoreScroll(s);
49542         this.afterMove(newIndex);
49543     },
49544
49545     afterMove : function(colIndex){
49546         if(this.enableMoveAnim && Roo.enableFx){
49547             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49548         }
49549         // if multisort - fix sortOrder, and reload..
49550         if (this.grid.dataSource.multiSort) {
49551             // the we can call sort again..
49552             var dm = this.grid.dataSource;
49553             var cm = this.grid.colModel;
49554             var so = [];
49555             for(var i = 0; i < cm.config.length; i++ ) {
49556                 
49557                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49558                     continue; // dont' bother, it's not in sort list or being set.
49559                 }
49560                 
49561                 so.push(cm.config[i].dataIndex);
49562             };
49563             dm.sortOrder = so;
49564             dm.load(dm.lastOptions);
49565             
49566             
49567         }
49568         
49569     },
49570
49571     updateCell : function(dm, rowIndex, dataIndex){
49572         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49573         if(typeof colIndex == "undefined"){ // not present in grid
49574             return;
49575         }
49576         var cm = this.grid.colModel;
49577         var cell = this.getCell(rowIndex, colIndex);
49578         var cellText = this.getCellText(rowIndex, colIndex);
49579
49580         var p = {
49581             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49582             id : cm.getColumnId(colIndex),
49583             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49584         };
49585         var renderer = cm.getRenderer(colIndex);
49586         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49587         if(typeof val == "undefined" || val === "") val = "&#160;";
49588         cellText.innerHTML = val;
49589         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49590         this.syncRowHeights(rowIndex, rowIndex);
49591     },
49592
49593     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49594         var maxWidth = 0;
49595         if(this.grid.autoSizeHeaders){
49596             var h = this.getHeaderCellMeasure(colIndex);
49597             maxWidth = Math.max(maxWidth, h.scrollWidth);
49598         }
49599         var tb, index;
49600         if(this.cm.isLocked(colIndex)){
49601             tb = this.getLockedTable();
49602             index = colIndex;
49603         }else{
49604             tb = this.getBodyTable();
49605             index = colIndex - this.cm.getLockedCount();
49606         }
49607         if(tb && tb.rows){
49608             var rows = tb.rows;
49609             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49610             for(var i = 0; i < stopIndex; i++){
49611                 var cell = rows[i].childNodes[index].firstChild;
49612                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49613             }
49614         }
49615         return maxWidth + /*margin for error in IE*/ 5;
49616     },
49617     /**
49618      * Autofit a column to its content.
49619      * @param {Number} colIndex
49620      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49621      */
49622      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49623          if(this.cm.isHidden(colIndex)){
49624              return; // can't calc a hidden column
49625          }
49626         if(forceMinSize){
49627             var cid = this.cm.getColumnId(colIndex);
49628             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49629            if(this.grid.autoSizeHeaders){
49630                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49631            }
49632         }
49633         var newWidth = this.calcColumnWidth(colIndex);
49634         this.cm.setColumnWidth(colIndex,
49635             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49636         if(!suppressEvent){
49637             this.grid.fireEvent("columnresize", colIndex, newWidth);
49638         }
49639     },
49640
49641     /**
49642      * Autofits all columns to their content and then expands to fit any extra space in the grid
49643      */
49644      autoSizeColumns : function(){
49645         var cm = this.grid.colModel;
49646         var colCount = cm.getColumnCount();
49647         for(var i = 0; i < colCount; i++){
49648             this.autoSizeColumn(i, true, true);
49649         }
49650         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49651             this.fitColumns();
49652         }else{
49653             this.updateColumns();
49654             this.layout();
49655         }
49656     },
49657
49658     /**
49659      * Autofits all columns to the grid's width proportionate with their current size
49660      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49661      */
49662     fitColumns : function(reserveScrollSpace){
49663         var cm = this.grid.colModel;
49664         var colCount = cm.getColumnCount();
49665         var cols = [];
49666         var width = 0;
49667         var i, w;
49668         for (i = 0; i < colCount; i++){
49669             if(!cm.isHidden(i) && !cm.isFixed(i)){
49670                 w = cm.getColumnWidth(i);
49671                 cols.push(i);
49672                 cols.push(w);
49673                 width += w;
49674             }
49675         }
49676         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49677         if(reserveScrollSpace){
49678             avail -= 17;
49679         }
49680         var frac = (avail - cm.getTotalWidth())/width;
49681         while (cols.length){
49682             w = cols.pop();
49683             i = cols.pop();
49684             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49685         }
49686         this.updateColumns();
49687         this.layout();
49688     },
49689
49690     onRowSelect : function(rowIndex){
49691         var row = this.getRowComposite(rowIndex);
49692         row.addClass("x-grid-row-selected");
49693     },
49694
49695     onRowDeselect : function(rowIndex){
49696         var row = this.getRowComposite(rowIndex);
49697         row.removeClass("x-grid-row-selected");
49698     },
49699
49700     onCellSelect : function(row, col){
49701         var cell = this.getCell(row, col);
49702         if(cell){
49703             Roo.fly(cell).addClass("x-grid-cell-selected");
49704         }
49705     },
49706
49707     onCellDeselect : function(row, col){
49708         var cell = this.getCell(row, col);
49709         if(cell){
49710             Roo.fly(cell).removeClass("x-grid-cell-selected");
49711         }
49712     },
49713
49714     updateHeaderSortState : function(){
49715         
49716         // sort state can be single { field: xxx, direction : yyy}
49717         // or   { xxx=>ASC , yyy : DESC ..... }
49718         
49719         var mstate = {};
49720         if (!this.ds.multiSort) { 
49721             var state = this.ds.getSortState();
49722             if(!state){
49723                 return;
49724             }
49725             mstate[state.field] = state.direction;
49726             // FIXME... - this is not used here.. but might be elsewhere..
49727             this.sortState = state;
49728             
49729         } else {
49730             mstate = this.ds.sortToggle;
49731         }
49732         //remove existing sort classes..
49733         
49734         var sc = this.sortClasses;
49735         var hds = this.el.select(this.headerSelector).removeClass(sc);
49736         
49737         for(var f in mstate) {
49738         
49739             var sortColumn = this.cm.findColumnIndex(f);
49740             
49741             if(sortColumn != -1){
49742                 var sortDir = mstate[f];        
49743                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49744             }
49745         }
49746         
49747          
49748         
49749     },
49750
49751
49752     handleHeaderClick : function(g, index){
49753         if(this.headersDisabled){
49754             return;
49755         }
49756         var dm = g.dataSource, cm = g.colModel;
49757         if(!cm.isSortable(index)){
49758             return;
49759         }
49760         g.stopEditing();
49761         
49762         if (dm.multiSort) {
49763             // update the sortOrder
49764             var so = [];
49765             for(var i = 0; i < cm.config.length; i++ ) {
49766                 
49767                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49768                     continue; // dont' bother, it's not in sort list or being set.
49769                 }
49770                 
49771                 so.push(cm.config[i].dataIndex);
49772             };
49773             dm.sortOrder = so;
49774         }
49775         
49776         
49777         dm.sort(cm.getDataIndex(index));
49778     },
49779
49780
49781     destroy : function(){
49782         if(this.colMenu){
49783             this.colMenu.removeAll();
49784             Roo.menu.MenuMgr.unregister(this.colMenu);
49785             this.colMenu.getEl().remove();
49786             delete this.colMenu;
49787         }
49788         if(this.hmenu){
49789             this.hmenu.removeAll();
49790             Roo.menu.MenuMgr.unregister(this.hmenu);
49791             this.hmenu.getEl().remove();
49792             delete this.hmenu;
49793         }
49794         if(this.grid.enableColumnMove){
49795             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49796             if(dds){
49797                 for(var dd in dds){
49798                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49799                         var elid = dds[dd].dragElId;
49800                         dds[dd].unreg();
49801                         Roo.get(elid).remove();
49802                     } else if(dds[dd].config.isTarget){
49803                         dds[dd].proxyTop.remove();
49804                         dds[dd].proxyBottom.remove();
49805                         dds[dd].unreg();
49806                     }
49807                     if(Roo.dd.DDM.locationCache[dd]){
49808                         delete Roo.dd.DDM.locationCache[dd];
49809                     }
49810                 }
49811                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49812             }
49813         }
49814         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49815         this.bind(null, null);
49816         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49817     },
49818
49819     handleLockChange : function(){
49820         this.refresh(true);
49821     },
49822
49823     onDenyColumnLock : function(){
49824
49825     },
49826
49827     onDenyColumnHide : function(){
49828
49829     },
49830
49831     handleHdMenuClick : function(item){
49832         var index = this.hdCtxIndex;
49833         var cm = this.cm, ds = this.ds;
49834         switch(item.id){
49835             case "asc":
49836                 ds.sort(cm.getDataIndex(index), "ASC");
49837                 break;
49838             case "desc":
49839                 ds.sort(cm.getDataIndex(index), "DESC");
49840                 break;
49841             case "lock":
49842                 var lc = cm.getLockedCount();
49843                 if(cm.getColumnCount(true) <= lc+1){
49844                     this.onDenyColumnLock();
49845                     return;
49846                 }
49847                 if(lc != index){
49848                     cm.setLocked(index, true, true);
49849                     cm.moveColumn(index, lc);
49850                     this.grid.fireEvent("columnmove", index, lc);
49851                 }else{
49852                     cm.setLocked(index, true);
49853                 }
49854             break;
49855             case "unlock":
49856                 var lc = cm.getLockedCount();
49857                 if((lc-1) != index){
49858                     cm.setLocked(index, false, true);
49859                     cm.moveColumn(index, lc-1);
49860                     this.grid.fireEvent("columnmove", index, lc-1);
49861                 }else{
49862                     cm.setLocked(index, false);
49863                 }
49864             break;
49865             default:
49866                 index = cm.getIndexById(item.id.substr(4));
49867                 if(index != -1){
49868                     if(item.checked && cm.getColumnCount(true) <= 1){
49869                         this.onDenyColumnHide();
49870                         return false;
49871                     }
49872                     cm.setHidden(index, item.checked);
49873                 }
49874         }
49875         return true;
49876     },
49877
49878     beforeColMenuShow : function(){
49879         var cm = this.cm,  colCount = cm.getColumnCount();
49880         this.colMenu.removeAll();
49881         for(var i = 0; i < colCount; i++){
49882             this.colMenu.add(new Roo.menu.CheckItem({
49883                 id: "col-"+cm.getColumnId(i),
49884                 text: cm.getColumnHeader(i),
49885                 checked: !cm.isHidden(i),
49886                 hideOnClick:false
49887             }));
49888         }
49889     },
49890
49891     handleHdCtx : function(g, index, e){
49892         e.stopEvent();
49893         var hd = this.getHeaderCell(index);
49894         this.hdCtxIndex = index;
49895         var ms = this.hmenu.items, cm = this.cm;
49896         ms.get("asc").setDisabled(!cm.isSortable(index));
49897         ms.get("desc").setDisabled(!cm.isSortable(index));
49898         if(this.grid.enableColLock !== false){
49899             ms.get("lock").setDisabled(cm.isLocked(index));
49900             ms.get("unlock").setDisabled(!cm.isLocked(index));
49901         }
49902         this.hmenu.show(hd, "tl-bl");
49903     },
49904
49905     handleHdOver : function(e){
49906         var hd = this.findHeaderCell(e.getTarget());
49907         if(hd && !this.headersDisabled){
49908             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49909                this.fly(hd).addClass("x-grid-hd-over");
49910             }
49911         }
49912     },
49913
49914     handleHdOut : function(e){
49915         var hd = this.findHeaderCell(e.getTarget());
49916         if(hd){
49917             this.fly(hd).removeClass("x-grid-hd-over");
49918         }
49919     },
49920
49921     handleSplitDblClick : function(e, t){
49922         var i = this.getCellIndex(t);
49923         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49924             this.autoSizeColumn(i, true);
49925             this.layout();
49926         }
49927     },
49928
49929     render : function(){
49930
49931         var cm = this.cm;
49932         var colCount = cm.getColumnCount();
49933
49934         if(this.grid.monitorWindowResize === true){
49935             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49936         }
49937         var header = this.renderHeaders();
49938         var body = this.templates.body.apply({rows:""});
49939         var html = this.templates.master.apply({
49940             lockedBody: body,
49941             body: body,
49942             lockedHeader: header[0],
49943             header: header[1]
49944         });
49945
49946         //this.updateColumns();
49947
49948         this.grid.getGridEl().dom.innerHTML = html;
49949
49950         this.initElements();
49951         
49952         // a kludge to fix the random scolling effect in webkit
49953         this.el.on("scroll", function() {
49954             this.el.dom.scrollTop=0; // hopefully not recursive..
49955         },this);
49956
49957         this.scroller.on("scroll", this.handleScroll, this);
49958         this.lockedBody.on("mousewheel", this.handleWheel, this);
49959         this.mainBody.on("mousewheel", this.handleWheel, this);
49960
49961         this.mainHd.on("mouseover", this.handleHdOver, this);
49962         this.mainHd.on("mouseout", this.handleHdOut, this);
49963         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49964                 {delegate: "."+this.splitClass});
49965
49966         this.lockedHd.on("mouseover", this.handleHdOver, this);
49967         this.lockedHd.on("mouseout", this.handleHdOut, this);
49968         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49969                 {delegate: "."+this.splitClass});
49970
49971         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49972             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49973         }
49974
49975         this.updateSplitters();
49976
49977         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49978             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49979             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49980         }
49981
49982         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49983             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49984             this.hmenu.add(
49985                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49986                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49987             );
49988             if(this.grid.enableColLock !== false){
49989                 this.hmenu.add('-',
49990                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49991                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49992                 );
49993             }
49994             if(this.grid.enableColumnHide !== false){
49995
49996                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49997                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49998                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49999
50000                 this.hmenu.add('-',
50001                     {id:"columns", text: this.columnsText, menu: this.colMenu}
50002                 );
50003             }
50004             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
50005
50006             this.grid.on("headercontextmenu", this.handleHdCtx, this);
50007         }
50008
50009         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
50010             this.dd = new Roo.grid.GridDragZone(this.grid, {
50011                 ddGroup : this.grid.ddGroup || 'GridDD'
50012             });
50013         }
50014
50015         /*
50016         for(var i = 0; i < colCount; i++){
50017             if(cm.isHidden(i)){
50018                 this.hideColumn(i);
50019             }
50020             if(cm.config[i].align){
50021                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
50022                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
50023             }
50024         }*/
50025         
50026         this.updateHeaderSortState();
50027
50028         this.beforeInitialResize();
50029         this.layout(true);
50030
50031         // two part rendering gives faster view to the user
50032         this.renderPhase2.defer(1, this);
50033     },
50034
50035     renderPhase2 : function(){
50036         // render the rows now
50037         this.refresh();
50038         if(this.grid.autoSizeColumns){
50039             this.autoSizeColumns();
50040         }
50041     },
50042
50043     beforeInitialResize : function(){
50044
50045     },
50046
50047     onColumnSplitterMoved : function(i, w){
50048         this.userResized = true;
50049         var cm = this.grid.colModel;
50050         cm.setColumnWidth(i, w, true);
50051         var cid = cm.getColumnId(i);
50052         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50053         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50054         this.updateSplitters();
50055         this.layout();
50056         this.grid.fireEvent("columnresize", i, w);
50057     },
50058
50059     syncRowHeights : function(startIndex, endIndex){
50060         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
50061             startIndex = startIndex || 0;
50062             var mrows = this.getBodyTable().rows;
50063             var lrows = this.getLockedTable().rows;
50064             var len = mrows.length-1;
50065             endIndex = Math.min(endIndex || len, len);
50066             for(var i = startIndex; i <= endIndex; i++){
50067                 var m = mrows[i], l = lrows[i];
50068                 var h = Math.max(m.offsetHeight, l.offsetHeight);
50069                 m.style.height = l.style.height = h + "px";
50070             }
50071         }
50072     },
50073
50074     layout : function(initialRender, is2ndPass){
50075         var g = this.grid;
50076         var auto = g.autoHeight;
50077         var scrollOffset = 16;
50078         var c = g.getGridEl(), cm = this.cm,
50079                 expandCol = g.autoExpandColumn,
50080                 gv = this;
50081         //c.beginMeasure();
50082
50083         if(!c.dom.offsetWidth){ // display:none?
50084             if(initialRender){
50085                 this.lockedWrap.show();
50086                 this.mainWrap.show();
50087             }
50088             return;
50089         }
50090
50091         var hasLock = this.cm.isLocked(0);
50092
50093         var tbh = this.headerPanel.getHeight();
50094         var bbh = this.footerPanel.getHeight();
50095
50096         if(auto){
50097             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
50098             var newHeight = ch + c.getBorderWidth("tb");
50099             if(g.maxHeight){
50100                 newHeight = Math.min(g.maxHeight, newHeight);
50101             }
50102             c.setHeight(newHeight);
50103         }
50104
50105         if(g.autoWidth){
50106             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
50107         }
50108
50109         var s = this.scroller;
50110
50111         var csize = c.getSize(true);
50112
50113         this.el.setSize(csize.width, csize.height);
50114
50115         this.headerPanel.setWidth(csize.width);
50116         this.footerPanel.setWidth(csize.width);
50117
50118         var hdHeight = this.mainHd.getHeight();
50119         var vw = csize.width;
50120         var vh = csize.height - (tbh + bbh);
50121
50122         s.setSize(vw, vh);
50123
50124         var bt = this.getBodyTable();
50125         var ltWidth = hasLock ?
50126                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
50127
50128         var scrollHeight = bt.offsetHeight;
50129         var scrollWidth = ltWidth + bt.offsetWidth;
50130         var vscroll = false, hscroll = false;
50131
50132         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
50133
50134         var lw = this.lockedWrap, mw = this.mainWrap;
50135         var lb = this.lockedBody, mb = this.mainBody;
50136
50137         setTimeout(function(){
50138             var t = s.dom.offsetTop;
50139             var w = s.dom.clientWidth,
50140                 h = s.dom.clientHeight;
50141
50142             lw.setTop(t);
50143             lw.setSize(ltWidth, h);
50144
50145             mw.setLeftTop(ltWidth, t);
50146             mw.setSize(w-ltWidth, h);
50147
50148             lb.setHeight(h-hdHeight);
50149             mb.setHeight(h-hdHeight);
50150
50151             if(is2ndPass !== true && !gv.userResized && expandCol){
50152                 // high speed resize without full column calculation
50153                 
50154                 var ci = cm.getIndexById(expandCol);
50155                 if (ci < 0) {
50156                     ci = cm.findColumnIndex(expandCol);
50157                 }
50158                 ci = Math.max(0, ci); // make sure it's got at least the first col.
50159                 var expandId = cm.getColumnId(ci);
50160                 var  tw = cm.getTotalWidth(false);
50161                 var currentWidth = cm.getColumnWidth(ci);
50162                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
50163                 if(currentWidth != cw){
50164                     cm.setColumnWidth(ci, cw, true);
50165                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50166                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50167                     gv.updateSplitters();
50168                     gv.layout(false, true);
50169                 }
50170             }
50171
50172             if(initialRender){
50173                 lw.show();
50174                 mw.show();
50175             }
50176             //c.endMeasure();
50177         }, 10);
50178     },
50179
50180     onWindowResize : function(){
50181         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50182             return;
50183         }
50184         this.layout();
50185     },
50186
50187     appendFooter : function(parentEl){
50188         return null;
50189     },
50190
50191     sortAscText : "Sort Ascending",
50192     sortDescText : "Sort Descending",
50193     lockText : "Lock Column",
50194     unlockText : "Unlock Column",
50195     columnsText : "Columns"
50196 });
50197
50198
50199 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50200     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50201     this.proxy.el.addClass('x-grid3-col-dd');
50202 };
50203
50204 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50205     handleMouseDown : function(e){
50206
50207     },
50208
50209     callHandleMouseDown : function(e){
50210         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50211     }
50212 });
50213 /*
50214  * Based on:
50215  * Ext JS Library 1.1.1
50216  * Copyright(c) 2006-2007, Ext JS, LLC.
50217  *
50218  * Originally Released Under LGPL - original licence link has changed is not relivant.
50219  *
50220  * Fork - LGPL
50221  * <script type="text/javascript">
50222  */
50223  
50224 // private
50225 // This is a support class used internally by the Grid components
50226 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50227     this.grid = grid;
50228     this.view = grid.getView();
50229     this.proxy = this.view.resizeProxy;
50230     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50231         "gridSplitters" + this.grid.getGridEl().id, {
50232         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50233     });
50234     this.setHandleElId(Roo.id(hd));
50235     this.setOuterHandleElId(Roo.id(hd2));
50236     this.scroll = false;
50237 };
50238 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50239     fly: Roo.Element.fly,
50240
50241     b4StartDrag : function(x, y){
50242         this.view.headersDisabled = true;
50243         this.proxy.setHeight(this.view.mainWrap.getHeight());
50244         var w = this.cm.getColumnWidth(this.cellIndex);
50245         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50246         this.resetConstraints();
50247         this.setXConstraint(minw, 1000);
50248         this.setYConstraint(0, 0);
50249         this.minX = x - minw;
50250         this.maxX = x + 1000;
50251         this.startPos = x;
50252         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50253     },
50254
50255
50256     handleMouseDown : function(e){
50257         ev = Roo.EventObject.setEvent(e);
50258         var t = this.fly(ev.getTarget());
50259         if(t.hasClass("x-grid-split")){
50260             this.cellIndex = this.view.getCellIndex(t.dom);
50261             this.split = t.dom;
50262             this.cm = this.grid.colModel;
50263             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50264                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50265             }
50266         }
50267     },
50268
50269     endDrag : function(e){
50270         this.view.headersDisabled = false;
50271         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50272         var diff = endX - this.startPos;
50273         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50274     },
50275
50276     autoOffset : function(){
50277         this.setDelta(0,0);
50278     }
50279 });/*
50280  * Based on:
50281  * Ext JS Library 1.1.1
50282  * Copyright(c) 2006-2007, Ext JS, LLC.
50283  *
50284  * Originally Released Under LGPL - original licence link has changed is not relivant.
50285  *
50286  * Fork - LGPL
50287  * <script type="text/javascript">
50288  */
50289  
50290 // private
50291 // This is a support class used internally by the Grid components
50292 Roo.grid.GridDragZone = function(grid, config){
50293     this.view = grid.getView();
50294     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50295     if(this.view.lockedBody){
50296         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50297         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50298     }
50299     this.scroll = false;
50300     this.grid = grid;
50301     this.ddel = document.createElement('div');
50302     this.ddel.className = 'x-grid-dd-wrap';
50303 };
50304
50305 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50306     ddGroup : "GridDD",
50307
50308     getDragData : function(e){
50309         var t = Roo.lib.Event.getTarget(e);
50310         var rowIndex = this.view.findRowIndex(t);
50311         if(rowIndex !== false){
50312             var sm = this.grid.selModel;
50313             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50314               //  sm.mouseDown(e, t);
50315             //}
50316             if (e.hasModifier()){
50317                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50318             }
50319             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50320         }
50321         return false;
50322     },
50323
50324     onInitDrag : function(e){
50325         var data = this.dragData;
50326         this.ddel.innerHTML = this.grid.getDragDropText();
50327         this.proxy.update(this.ddel);
50328         // fire start drag?
50329     },
50330
50331     afterRepair : function(){
50332         this.dragging = false;
50333     },
50334
50335     getRepairXY : function(e, data){
50336         return false;
50337     },
50338
50339     onEndDrag : function(data, e){
50340         // fire end drag?
50341     },
50342
50343     onValidDrop : function(dd, e, id){
50344         // fire drag drop?
50345         this.hideProxy();
50346     },
50347
50348     beforeInvalidDrop : function(e, id){
50349
50350     }
50351 });/*
50352  * Based on:
50353  * Ext JS Library 1.1.1
50354  * Copyright(c) 2006-2007, Ext JS, LLC.
50355  *
50356  * Originally Released Under LGPL - original licence link has changed is not relivant.
50357  *
50358  * Fork - LGPL
50359  * <script type="text/javascript">
50360  */
50361  
50362
50363 /**
50364  * @class Roo.grid.ColumnModel
50365  * @extends Roo.util.Observable
50366  * This is the default implementation of a ColumnModel used by the Grid. It defines
50367  * the columns in the grid.
50368  * <br>Usage:<br>
50369  <pre><code>
50370  var colModel = new Roo.grid.ColumnModel([
50371         {header: "Ticker", width: 60, sortable: true, locked: true},
50372         {header: "Company Name", width: 150, sortable: true},
50373         {header: "Market Cap.", width: 100, sortable: true},
50374         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50375         {header: "Employees", width: 100, sortable: true, resizable: false}
50376  ]);
50377  </code></pre>
50378  * <p>
50379  
50380  * The config options listed for this class are options which may appear in each
50381  * individual column definition.
50382  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50383  * @constructor
50384  * @param {Object} config An Array of column config objects. See this class's
50385  * config objects for details.
50386 */
50387 Roo.grid.ColumnModel = function(config){
50388         /**
50389      * The config passed into the constructor
50390      */
50391     this.config = config;
50392     this.lookup = {};
50393
50394     // if no id, create one
50395     // if the column does not have a dataIndex mapping,
50396     // map it to the order it is in the config
50397     for(var i = 0, len = config.length; i < len; i++){
50398         var c = config[i];
50399         if(typeof c.dataIndex == "undefined"){
50400             c.dataIndex = i;
50401         }
50402         if(typeof c.renderer == "string"){
50403             c.renderer = Roo.util.Format[c.renderer];
50404         }
50405         if(typeof c.id == "undefined"){
50406             c.id = Roo.id();
50407         }
50408         if(c.editor && c.editor.xtype){
50409             c.editor  = Roo.factory(c.editor, Roo.grid);
50410         }
50411         if(c.editor && c.editor.isFormField){
50412             c.editor = new Roo.grid.GridEditor(c.editor);
50413         }
50414         this.lookup[c.id] = c;
50415     }
50416
50417     /**
50418      * The width of columns which have no width specified (defaults to 100)
50419      * @type Number
50420      */
50421     this.defaultWidth = 100;
50422
50423     /**
50424      * Default sortable of columns which have no sortable specified (defaults to false)
50425      * @type Boolean
50426      */
50427     this.defaultSortable = false;
50428
50429     this.addEvents({
50430         /**
50431              * @event widthchange
50432              * Fires when the width of a column changes.
50433              * @param {ColumnModel} this
50434              * @param {Number} columnIndex The column index
50435              * @param {Number} newWidth The new width
50436              */
50437             "widthchange": true,
50438         /**
50439              * @event headerchange
50440              * Fires when the text of a header changes.
50441              * @param {ColumnModel} this
50442              * @param {Number} columnIndex The column index
50443              * @param {Number} newText The new header text
50444              */
50445             "headerchange": true,
50446         /**
50447              * @event hiddenchange
50448              * Fires when a column is hidden or "unhidden".
50449              * @param {ColumnModel} this
50450              * @param {Number} columnIndex The column index
50451              * @param {Boolean} hidden true if hidden, false otherwise
50452              */
50453             "hiddenchange": true,
50454             /**
50455          * @event columnmoved
50456          * Fires when a column is moved.
50457          * @param {ColumnModel} this
50458          * @param {Number} oldIndex
50459          * @param {Number} newIndex
50460          */
50461         "columnmoved" : true,
50462         /**
50463          * @event columlockchange
50464          * Fires when a column's locked state is changed
50465          * @param {ColumnModel} this
50466          * @param {Number} colIndex
50467          * @param {Boolean} locked true if locked
50468          */
50469         "columnlockchange" : true
50470     });
50471     Roo.grid.ColumnModel.superclass.constructor.call(this);
50472 };
50473 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50474     /**
50475      * @cfg {String} header The header text to display in the Grid view.
50476      */
50477     /**
50478      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50479      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50480      * specified, the column's index is used as an index into the Record's data Array.
50481      */
50482     /**
50483      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50484      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50485      */
50486     /**
50487      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50488      * Defaults to the value of the {@link #defaultSortable} property.
50489      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50490      */
50491     /**
50492      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50493      */
50494     /**
50495      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50496      */
50497     /**
50498      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50499      */
50500     /**
50501      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50502      */
50503     /**
50504      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50505      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50506      * default renderer uses the raw data value.
50507      */
50508        /**
50509      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50510      */
50511     /**
50512      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50513      */
50514
50515     /**
50516      * Returns the id of the column at the specified index.
50517      * @param {Number} index The column index
50518      * @return {String} the id
50519      */
50520     getColumnId : function(index){
50521         return this.config[index].id;
50522     },
50523
50524     /**
50525      * Returns the column for a specified id.
50526      * @param {String} id The column id
50527      * @return {Object} the column
50528      */
50529     getColumnById : function(id){
50530         return this.lookup[id];
50531     },
50532
50533     
50534     /**
50535      * Returns the column for a specified dataIndex.
50536      * @param {String} dataIndex The column dataIndex
50537      * @return {Object|Boolean} the column or false if not found
50538      */
50539     getColumnByDataIndex: function(dataIndex){
50540         var index = this.findColumnIndex(dataIndex);
50541         return index > -1 ? this.config[index] : false;
50542     },
50543     
50544     /**
50545      * Returns the index for a specified column id.
50546      * @param {String} id The column id
50547      * @return {Number} the index, or -1 if not found
50548      */
50549     getIndexById : function(id){
50550         for(var i = 0, len = this.config.length; i < len; i++){
50551             if(this.config[i].id == id){
50552                 return i;
50553             }
50554         }
50555         return -1;
50556     },
50557     
50558     /**
50559      * Returns the index for a specified column dataIndex.
50560      * @param {String} dataIndex The column dataIndex
50561      * @return {Number} the index, or -1 if not found
50562      */
50563     
50564     findColumnIndex : function(dataIndex){
50565         for(var i = 0, len = this.config.length; i < len; i++){
50566             if(this.config[i].dataIndex == dataIndex){
50567                 return i;
50568             }
50569         }
50570         return -1;
50571     },
50572     
50573     
50574     moveColumn : function(oldIndex, newIndex){
50575         var c = this.config[oldIndex];
50576         this.config.splice(oldIndex, 1);
50577         this.config.splice(newIndex, 0, c);
50578         this.dataMap = null;
50579         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50580     },
50581
50582     isLocked : function(colIndex){
50583         return this.config[colIndex].locked === true;
50584     },
50585
50586     setLocked : function(colIndex, value, suppressEvent){
50587         if(this.isLocked(colIndex) == value){
50588             return;
50589         }
50590         this.config[colIndex].locked = value;
50591         if(!suppressEvent){
50592             this.fireEvent("columnlockchange", this, colIndex, value);
50593         }
50594     },
50595
50596     getTotalLockedWidth : function(){
50597         var totalWidth = 0;
50598         for(var i = 0; i < this.config.length; i++){
50599             if(this.isLocked(i) && !this.isHidden(i)){
50600                 this.totalWidth += this.getColumnWidth(i);
50601             }
50602         }
50603         return totalWidth;
50604     },
50605
50606     getLockedCount : function(){
50607         for(var i = 0, len = this.config.length; i < len; i++){
50608             if(!this.isLocked(i)){
50609                 return i;
50610             }
50611         }
50612     },
50613
50614     /**
50615      * Returns the number of columns.
50616      * @return {Number}
50617      */
50618     getColumnCount : function(visibleOnly){
50619         if(visibleOnly === true){
50620             var c = 0;
50621             for(var i = 0, len = this.config.length; i < len; i++){
50622                 if(!this.isHidden(i)){
50623                     c++;
50624                 }
50625             }
50626             return c;
50627         }
50628         return this.config.length;
50629     },
50630
50631     /**
50632      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50633      * @param {Function} fn
50634      * @param {Object} scope (optional)
50635      * @return {Array} result
50636      */
50637     getColumnsBy : function(fn, scope){
50638         var r = [];
50639         for(var i = 0, len = this.config.length; i < len; i++){
50640             var c = this.config[i];
50641             if(fn.call(scope||this, c, i) === true){
50642                 r[r.length] = c;
50643             }
50644         }
50645         return r;
50646     },
50647
50648     /**
50649      * Returns true if the specified column is sortable.
50650      * @param {Number} col The column index
50651      * @return {Boolean}
50652      */
50653     isSortable : function(col){
50654         if(typeof this.config[col].sortable == "undefined"){
50655             return this.defaultSortable;
50656         }
50657         return this.config[col].sortable;
50658     },
50659
50660     /**
50661      * Returns the rendering (formatting) function defined for the column.
50662      * @param {Number} col The column index.
50663      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50664      */
50665     getRenderer : function(col){
50666         if(!this.config[col].renderer){
50667             return Roo.grid.ColumnModel.defaultRenderer;
50668         }
50669         return this.config[col].renderer;
50670     },
50671
50672     /**
50673      * Sets the rendering (formatting) function for a column.
50674      * @param {Number} col The column index
50675      * @param {Function} fn The function to use to process the cell's raw data
50676      * to return HTML markup for the grid view. The render function is called with
50677      * the following parameters:<ul>
50678      * <li>Data value.</li>
50679      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50680      * <li>css A CSS style string to apply to the table cell.</li>
50681      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50682      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50683      * <li>Row index</li>
50684      * <li>Column index</li>
50685      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50686      */
50687     setRenderer : function(col, fn){
50688         this.config[col].renderer = fn;
50689     },
50690
50691     /**
50692      * Returns the width for the specified column.
50693      * @param {Number} col The column index
50694      * @return {Number}
50695      */
50696     getColumnWidth : function(col){
50697         return this.config[col].width * 1 || this.defaultWidth;
50698     },
50699
50700     /**
50701      * Sets the width for a column.
50702      * @param {Number} col The column index
50703      * @param {Number} width The new width
50704      */
50705     setColumnWidth : function(col, width, suppressEvent){
50706         this.config[col].width = width;
50707         this.totalWidth = null;
50708         if(!suppressEvent){
50709              this.fireEvent("widthchange", this, col, width);
50710         }
50711     },
50712
50713     /**
50714      * Returns the total width of all columns.
50715      * @param {Boolean} includeHidden True to include hidden column widths
50716      * @return {Number}
50717      */
50718     getTotalWidth : function(includeHidden){
50719         if(!this.totalWidth){
50720             this.totalWidth = 0;
50721             for(var i = 0, len = this.config.length; i < len; i++){
50722                 if(includeHidden || !this.isHidden(i)){
50723                     this.totalWidth += this.getColumnWidth(i);
50724                 }
50725             }
50726         }
50727         return this.totalWidth;
50728     },
50729
50730     /**
50731      * Returns the header for the specified column.
50732      * @param {Number} col The column index
50733      * @return {String}
50734      */
50735     getColumnHeader : function(col){
50736         return this.config[col].header;
50737     },
50738
50739     /**
50740      * Sets the header for a column.
50741      * @param {Number} col The column index
50742      * @param {String} header The new header
50743      */
50744     setColumnHeader : function(col, header){
50745         this.config[col].header = header;
50746         this.fireEvent("headerchange", this, col, header);
50747     },
50748
50749     /**
50750      * Returns the tooltip for the specified column.
50751      * @param {Number} col The column index
50752      * @return {String}
50753      */
50754     getColumnTooltip : function(col){
50755             return this.config[col].tooltip;
50756     },
50757     /**
50758      * Sets the tooltip for a column.
50759      * @param {Number} col The column index
50760      * @param {String} tooltip The new tooltip
50761      */
50762     setColumnTooltip : function(col, tooltip){
50763             this.config[col].tooltip = tooltip;
50764     },
50765
50766     /**
50767      * Returns the dataIndex for the specified column.
50768      * @param {Number} col The column index
50769      * @return {Number}
50770      */
50771     getDataIndex : function(col){
50772         return this.config[col].dataIndex;
50773     },
50774
50775     /**
50776      * Sets the dataIndex for a column.
50777      * @param {Number} col The column index
50778      * @param {Number} dataIndex The new dataIndex
50779      */
50780     setDataIndex : function(col, dataIndex){
50781         this.config[col].dataIndex = dataIndex;
50782     },
50783
50784     
50785     
50786     /**
50787      * Returns true if the cell is editable.
50788      * @param {Number} colIndex The column index
50789      * @param {Number} rowIndex The row index
50790      * @return {Boolean}
50791      */
50792     isCellEditable : function(colIndex, rowIndex){
50793         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50794     },
50795
50796     /**
50797      * Returns the editor defined for the cell/column.
50798      * return false or null to disable editing.
50799      * @param {Number} colIndex The column index
50800      * @param {Number} rowIndex The row index
50801      * @return {Object}
50802      */
50803     getCellEditor : function(colIndex, rowIndex){
50804         return this.config[colIndex].editor;
50805     },
50806
50807     /**
50808      * Sets if a column is editable.
50809      * @param {Number} col The column index
50810      * @param {Boolean} editable True if the column is editable
50811      */
50812     setEditable : function(col, editable){
50813         this.config[col].editable = editable;
50814     },
50815
50816
50817     /**
50818      * Returns true if the column is hidden.
50819      * @param {Number} colIndex The column index
50820      * @return {Boolean}
50821      */
50822     isHidden : function(colIndex){
50823         return this.config[colIndex].hidden;
50824     },
50825
50826
50827     /**
50828      * Returns true if the column width cannot be changed
50829      */
50830     isFixed : function(colIndex){
50831         return this.config[colIndex].fixed;
50832     },
50833
50834     /**
50835      * Returns true if the column can be resized
50836      * @return {Boolean}
50837      */
50838     isResizable : function(colIndex){
50839         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50840     },
50841     /**
50842      * Sets if a column is hidden.
50843      * @param {Number} colIndex The column index
50844      * @param {Boolean} hidden True if the column is hidden
50845      */
50846     setHidden : function(colIndex, hidden){
50847         this.config[colIndex].hidden = hidden;
50848         this.totalWidth = null;
50849         this.fireEvent("hiddenchange", this, colIndex, hidden);
50850     },
50851
50852     /**
50853      * Sets the editor for a column.
50854      * @param {Number} col The column index
50855      * @param {Object} editor The editor object
50856      */
50857     setEditor : function(col, editor){
50858         this.config[col].editor = editor;
50859     }
50860 });
50861
50862 Roo.grid.ColumnModel.defaultRenderer = function(value){
50863         if(typeof value == "string" && value.length < 1){
50864             return "&#160;";
50865         }
50866         return value;
50867 };
50868
50869 // Alias for backwards compatibility
50870 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50871 /*
50872  * Based on:
50873  * Ext JS Library 1.1.1
50874  * Copyright(c) 2006-2007, Ext JS, LLC.
50875  *
50876  * Originally Released Under LGPL - original licence link has changed is not relivant.
50877  *
50878  * Fork - LGPL
50879  * <script type="text/javascript">
50880  */
50881
50882 /**
50883  * @class Roo.grid.AbstractSelectionModel
50884  * @extends Roo.util.Observable
50885  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50886  * implemented by descendant classes.  This class should not be directly instantiated.
50887  * @constructor
50888  */
50889 Roo.grid.AbstractSelectionModel = function(){
50890     this.locked = false;
50891     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50892 };
50893
50894 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50895     /** @ignore Called by the grid automatically. Do not call directly. */
50896     init : function(grid){
50897         this.grid = grid;
50898         this.initEvents();
50899     },
50900
50901     /**
50902      * Locks the selections.
50903      */
50904     lock : function(){
50905         this.locked = true;
50906     },
50907
50908     /**
50909      * Unlocks the selections.
50910      */
50911     unlock : function(){
50912         this.locked = false;
50913     },
50914
50915     /**
50916      * Returns true if the selections are locked.
50917      * @return {Boolean}
50918      */
50919     isLocked : function(){
50920         return this.locked;
50921     }
50922 });/*
50923  * Based on:
50924  * Ext JS Library 1.1.1
50925  * Copyright(c) 2006-2007, Ext JS, LLC.
50926  *
50927  * Originally Released Under LGPL - original licence link has changed is not relivant.
50928  *
50929  * Fork - LGPL
50930  * <script type="text/javascript">
50931  */
50932 /**
50933  * @extends Roo.grid.AbstractSelectionModel
50934  * @class Roo.grid.RowSelectionModel
50935  * The default SelectionModel used by {@link Roo.grid.Grid}.
50936  * It supports multiple selections and keyboard selection/navigation. 
50937  * @constructor
50938  * @param {Object} config
50939  */
50940 Roo.grid.RowSelectionModel = function(config){
50941     Roo.apply(this, config);
50942     this.selections = new Roo.util.MixedCollection(false, function(o){
50943         return o.id;
50944     });
50945
50946     this.last = false;
50947     this.lastActive = false;
50948
50949     this.addEvents({
50950         /**
50951              * @event selectionchange
50952              * Fires when the selection changes
50953              * @param {SelectionModel} this
50954              */
50955             "selectionchange" : true,
50956         /**
50957              * @event afterselectionchange
50958              * Fires after the selection changes (eg. by key press or clicking)
50959              * @param {SelectionModel} this
50960              */
50961             "afterselectionchange" : true,
50962         /**
50963              * @event beforerowselect
50964              * Fires when a row is selected being selected, return false to cancel.
50965              * @param {SelectionModel} this
50966              * @param {Number} rowIndex The selected index
50967              * @param {Boolean} keepExisting False if other selections will be cleared
50968              */
50969             "beforerowselect" : true,
50970         /**
50971              * @event rowselect
50972              * Fires when a row is selected.
50973              * @param {SelectionModel} this
50974              * @param {Number} rowIndex The selected index
50975              * @param {Roo.data.Record} r The record
50976              */
50977             "rowselect" : true,
50978         /**
50979              * @event rowdeselect
50980              * Fires when a row is deselected.
50981              * @param {SelectionModel} this
50982              * @param {Number} rowIndex The selected index
50983              */
50984         "rowdeselect" : true
50985     });
50986     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50987     this.locked = false;
50988 };
50989
50990 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50991     /**
50992      * @cfg {Boolean} singleSelect
50993      * True to allow selection of only one row at a time (defaults to false)
50994      */
50995     singleSelect : false,
50996
50997     // private
50998     initEvents : function(){
50999
51000         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
51001             this.grid.on("mousedown", this.handleMouseDown, this);
51002         }else{ // allow click to work like normal
51003             this.grid.on("rowclick", this.handleDragableRowClick, this);
51004         }
51005
51006         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
51007             "up" : function(e){
51008                 if(!e.shiftKey){
51009                     this.selectPrevious(e.shiftKey);
51010                 }else if(this.last !== false && this.lastActive !== false){
51011                     var last = this.last;
51012                     this.selectRange(this.last,  this.lastActive-1);
51013                     this.grid.getView().focusRow(this.lastActive);
51014                     if(last !== false){
51015                         this.last = last;
51016                     }
51017                 }else{
51018                     this.selectFirstRow();
51019                 }
51020                 this.fireEvent("afterselectionchange", this);
51021             },
51022             "down" : function(e){
51023                 if(!e.shiftKey){
51024                     this.selectNext(e.shiftKey);
51025                 }else if(this.last !== false && this.lastActive !== false){
51026                     var last = this.last;
51027                     this.selectRange(this.last,  this.lastActive+1);
51028                     this.grid.getView().focusRow(this.lastActive);
51029                     if(last !== false){
51030                         this.last = last;
51031                     }
51032                 }else{
51033                     this.selectFirstRow();
51034                 }
51035                 this.fireEvent("afterselectionchange", this);
51036             },
51037             scope: this
51038         });
51039
51040         var view = this.grid.view;
51041         view.on("refresh", this.onRefresh, this);
51042         view.on("rowupdated", this.onRowUpdated, this);
51043         view.on("rowremoved", this.onRemove, this);
51044     },
51045
51046     // private
51047     onRefresh : function(){
51048         var ds = this.grid.dataSource, i, v = this.grid.view;
51049         var s = this.selections;
51050         s.each(function(r){
51051             if((i = ds.indexOfId(r.id)) != -1){
51052                 v.onRowSelect(i);
51053             }else{
51054                 s.remove(r);
51055             }
51056         });
51057     },
51058
51059     // private
51060     onRemove : function(v, index, r){
51061         this.selections.remove(r);
51062     },
51063
51064     // private
51065     onRowUpdated : function(v, index, r){
51066         if(this.isSelected(r)){
51067             v.onRowSelect(index);
51068         }
51069     },
51070
51071     /**
51072      * Select records.
51073      * @param {Array} records The records to select
51074      * @param {Boolean} keepExisting (optional) True to keep existing selections
51075      */
51076     selectRecords : function(records, keepExisting){
51077         if(!keepExisting){
51078             this.clearSelections();
51079         }
51080         var ds = this.grid.dataSource;
51081         for(var i = 0, len = records.length; i < len; i++){
51082             this.selectRow(ds.indexOf(records[i]), true);
51083         }
51084     },
51085
51086     /**
51087      * Gets the number of selected rows.
51088      * @return {Number}
51089      */
51090     getCount : function(){
51091         return this.selections.length;
51092     },
51093
51094     /**
51095      * Selects the first row in the grid.
51096      */
51097     selectFirstRow : function(){
51098         this.selectRow(0);
51099     },
51100
51101     /**
51102      * Select the last row.
51103      * @param {Boolean} keepExisting (optional) True to keep existing selections
51104      */
51105     selectLastRow : function(keepExisting){
51106         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
51107     },
51108
51109     /**
51110      * Selects the row immediately following the last selected row.
51111      * @param {Boolean} keepExisting (optional) True to keep existing selections
51112      */
51113     selectNext : function(keepExisting){
51114         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
51115             this.selectRow(this.last+1, keepExisting);
51116             this.grid.getView().focusRow(this.last);
51117         }
51118     },
51119
51120     /**
51121      * Selects the row that precedes the last selected row.
51122      * @param {Boolean} keepExisting (optional) True to keep existing selections
51123      */
51124     selectPrevious : function(keepExisting){
51125         if(this.last){
51126             this.selectRow(this.last-1, keepExisting);
51127             this.grid.getView().focusRow(this.last);
51128         }
51129     },
51130
51131     /**
51132      * Returns the selected records
51133      * @return {Array} Array of selected records
51134      */
51135     getSelections : function(){
51136         return [].concat(this.selections.items);
51137     },
51138
51139     /**
51140      * Returns the first selected record.
51141      * @return {Record}
51142      */
51143     getSelected : function(){
51144         return this.selections.itemAt(0);
51145     },
51146
51147
51148     /**
51149      * Clears all selections.
51150      */
51151     clearSelections : function(fast){
51152         if(this.locked) return;
51153         if(fast !== true){
51154             var ds = this.grid.dataSource;
51155             var s = this.selections;
51156             s.each(function(r){
51157                 this.deselectRow(ds.indexOfId(r.id));
51158             }, this);
51159             s.clear();
51160         }else{
51161             this.selections.clear();
51162         }
51163         this.last = false;
51164     },
51165
51166
51167     /**
51168      * Selects all rows.
51169      */
51170     selectAll : function(){
51171         if(this.locked) return;
51172         this.selections.clear();
51173         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51174             this.selectRow(i, true);
51175         }
51176     },
51177
51178     /**
51179      * Returns True if there is a selection.
51180      * @return {Boolean}
51181      */
51182     hasSelection : function(){
51183         return this.selections.length > 0;
51184     },
51185
51186     /**
51187      * Returns True if the specified row is selected.
51188      * @param {Number/Record} record The record or index of the record to check
51189      * @return {Boolean}
51190      */
51191     isSelected : function(index){
51192         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51193         return (r && this.selections.key(r.id) ? true : false);
51194     },
51195
51196     /**
51197      * Returns True if the specified record id is selected.
51198      * @param {String} id The id of record to check
51199      * @return {Boolean}
51200      */
51201     isIdSelected : function(id){
51202         return (this.selections.key(id) ? true : false);
51203     },
51204
51205     // private
51206     handleMouseDown : function(e, t){
51207         var view = this.grid.getView(), rowIndex;
51208         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51209             return;
51210         };
51211         if(e.shiftKey && this.last !== false){
51212             var last = this.last;
51213             this.selectRange(last, rowIndex, e.ctrlKey);
51214             this.last = last; // reset the last
51215             view.focusRow(rowIndex);
51216         }else{
51217             var isSelected = this.isSelected(rowIndex);
51218             if(e.button !== 0 && isSelected){
51219                 view.focusRow(rowIndex);
51220             }else if(e.ctrlKey && isSelected){
51221                 this.deselectRow(rowIndex);
51222             }else if(!isSelected){
51223                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51224                 view.focusRow(rowIndex);
51225             }
51226         }
51227         this.fireEvent("afterselectionchange", this);
51228     },
51229     // private
51230     handleDragableRowClick :  function(grid, rowIndex, e) 
51231     {
51232         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51233             this.selectRow(rowIndex, false);
51234             grid.view.focusRow(rowIndex);
51235              this.fireEvent("afterselectionchange", this);
51236         }
51237     },
51238     
51239     /**
51240      * Selects multiple rows.
51241      * @param {Array} rows Array of the indexes of the row to select
51242      * @param {Boolean} keepExisting (optional) True to keep existing selections
51243      */
51244     selectRows : function(rows, keepExisting){
51245         if(!keepExisting){
51246             this.clearSelections();
51247         }
51248         for(var i = 0, len = rows.length; i < len; i++){
51249             this.selectRow(rows[i], true);
51250         }
51251     },
51252
51253     /**
51254      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51255      * @param {Number} startRow The index of the first row in the range
51256      * @param {Number} endRow The index of the last row in the range
51257      * @param {Boolean} keepExisting (optional) True to retain existing selections
51258      */
51259     selectRange : function(startRow, endRow, keepExisting){
51260         if(this.locked) return;
51261         if(!keepExisting){
51262             this.clearSelections();
51263         }
51264         if(startRow <= endRow){
51265             for(var i = startRow; i <= endRow; i++){
51266                 this.selectRow(i, true);
51267             }
51268         }else{
51269             for(var i = startRow; i >= endRow; i--){
51270                 this.selectRow(i, true);
51271             }
51272         }
51273     },
51274
51275     /**
51276      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51277      * @param {Number} startRow The index of the first row in the range
51278      * @param {Number} endRow The index of the last row in the range
51279      */
51280     deselectRange : function(startRow, endRow, preventViewNotify){
51281         if(this.locked) return;
51282         for(var i = startRow; i <= endRow; i++){
51283             this.deselectRow(i, preventViewNotify);
51284         }
51285     },
51286
51287     /**
51288      * Selects a row.
51289      * @param {Number} row The index of the row to select
51290      * @param {Boolean} keepExisting (optional) True to keep existing selections
51291      */
51292     selectRow : function(index, keepExisting, preventViewNotify){
51293         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51294         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51295             if(!keepExisting || this.singleSelect){
51296                 this.clearSelections();
51297             }
51298             var r = this.grid.dataSource.getAt(index);
51299             this.selections.add(r);
51300             this.last = this.lastActive = index;
51301             if(!preventViewNotify){
51302                 this.grid.getView().onRowSelect(index);
51303             }
51304             this.fireEvent("rowselect", this, index, r);
51305             this.fireEvent("selectionchange", this);
51306         }
51307     },
51308
51309     /**
51310      * Deselects a row.
51311      * @param {Number} row The index of the row to deselect
51312      */
51313     deselectRow : function(index, preventViewNotify){
51314         if(this.locked) return;
51315         if(this.last == index){
51316             this.last = false;
51317         }
51318         if(this.lastActive == index){
51319             this.lastActive = false;
51320         }
51321         var r = this.grid.dataSource.getAt(index);
51322         this.selections.remove(r);
51323         if(!preventViewNotify){
51324             this.grid.getView().onRowDeselect(index);
51325         }
51326         this.fireEvent("rowdeselect", this, index);
51327         this.fireEvent("selectionchange", this);
51328     },
51329
51330     // private
51331     restoreLast : function(){
51332         if(this._last){
51333             this.last = this._last;
51334         }
51335     },
51336
51337     // private
51338     acceptsNav : function(row, col, cm){
51339         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51340     },
51341
51342     // private
51343     onEditorKey : function(field, e){
51344         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51345         if(k == e.TAB){
51346             e.stopEvent();
51347             ed.completeEdit();
51348             if(e.shiftKey){
51349                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51350             }else{
51351                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51352             }
51353         }else if(k == e.ENTER && !e.ctrlKey){
51354             e.stopEvent();
51355             ed.completeEdit();
51356             if(e.shiftKey){
51357                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51358             }else{
51359                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51360             }
51361         }else if(k == e.ESC){
51362             ed.cancelEdit();
51363         }
51364         if(newCell){
51365             g.startEditing(newCell[0], newCell[1]);
51366         }
51367     }
51368 });/*
51369  * Based on:
51370  * Ext JS Library 1.1.1
51371  * Copyright(c) 2006-2007, Ext JS, LLC.
51372  *
51373  * Originally Released Under LGPL - original licence link has changed is not relivant.
51374  *
51375  * Fork - LGPL
51376  * <script type="text/javascript">
51377  */
51378 /**
51379  * @class Roo.grid.CellSelectionModel
51380  * @extends Roo.grid.AbstractSelectionModel
51381  * This class provides the basic implementation for cell selection in a grid.
51382  * @constructor
51383  * @param {Object} config The object containing the configuration of this model.
51384  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51385  */
51386 Roo.grid.CellSelectionModel = function(config){
51387     Roo.apply(this, config);
51388
51389     this.selection = null;
51390
51391     this.addEvents({
51392         /**
51393              * @event beforerowselect
51394              * Fires before a cell is selected.
51395              * @param {SelectionModel} this
51396              * @param {Number} rowIndex The selected row index
51397              * @param {Number} colIndex The selected cell index
51398              */
51399             "beforecellselect" : true,
51400         /**
51401              * @event cellselect
51402              * Fires when a cell is selected.
51403              * @param {SelectionModel} this
51404              * @param {Number} rowIndex The selected row index
51405              * @param {Number} colIndex The selected cell index
51406              */
51407             "cellselect" : true,
51408         /**
51409              * @event selectionchange
51410              * Fires when the active selection changes.
51411              * @param {SelectionModel} this
51412              * @param {Object} selection null for no selection or an object (o) with two properties
51413                 <ul>
51414                 <li>o.record: the record object for the row the selection is in</li>
51415                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51416                 </ul>
51417              */
51418             "selectionchange" : true,
51419         /**
51420              * @event tabend
51421              * Fires when the tab (or enter) was pressed on the last editable cell
51422              * You can use this to trigger add new row.
51423              * @param {SelectionModel} this
51424              */
51425             "tabend" : true,
51426          /**
51427              * @event beforeeditnext
51428              * Fires before the next editable sell is made active
51429              * You can use this to skip to another cell or fire the tabend
51430              *    if you set cell to false
51431              * @param {Object} eventdata object : { cell : [ row, col ] } 
51432              */
51433             "beforeeditnext" : true
51434     });
51435     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51436 };
51437
51438 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51439     
51440     enter_is_tab: false,
51441
51442     /** @ignore */
51443     initEvents : function(){
51444         this.grid.on("mousedown", this.handleMouseDown, this);
51445         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51446         var view = this.grid.view;
51447         view.on("refresh", this.onViewChange, this);
51448         view.on("rowupdated", this.onRowUpdated, this);
51449         view.on("beforerowremoved", this.clearSelections, this);
51450         view.on("beforerowsinserted", this.clearSelections, this);
51451         if(this.grid.isEditor){
51452             this.grid.on("beforeedit", this.beforeEdit,  this);
51453         }
51454     },
51455
51456         //private
51457     beforeEdit : function(e){
51458         this.select(e.row, e.column, false, true, e.record);
51459     },
51460
51461         //private
51462     onRowUpdated : function(v, index, r){
51463         if(this.selection && this.selection.record == r){
51464             v.onCellSelect(index, this.selection.cell[1]);
51465         }
51466     },
51467
51468         //private
51469     onViewChange : function(){
51470         this.clearSelections(true);
51471     },
51472
51473         /**
51474          * Returns the currently selected cell,.
51475          * @return {Array} The selected cell (row, column) or null if none selected.
51476          */
51477     getSelectedCell : function(){
51478         return this.selection ? this.selection.cell : null;
51479     },
51480
51481     /**
51482      * Clears all selections.
51483      * @param {Boolean} true to prevent the gridview from being notified about the change.
51484      */
51485     clearSelections : function(preventNotify){
51486         var s = this.selection;
51487         if(s){
51488             if(preventNotify !== true){
51489                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51490             }
51491             this.selection = null;
51492             this.fireEvent("selectionchange", this, null);
51493         }
51494     },
51495
51496     /**
51497      * Returns true if there is a selection.
51498      * @return {Boolean}
51499      */
51500     hasSelection : function(){
51501         return this.selection ? true : false;
51502     },
51503
51504     /** @ignore */
51505     handleMouseDown : function(e, t){
51506         var v = this.grid.getView();
51507         if(this.isLocked()){
51508             return;
51509         };
51510         var row = v.findRowIndex(t);
51511         var cell = v.findCellIndex(t);
51512         if(row !== false && cell !== false){
51513             this.select(row, cell);
51514         }
51515     },
51516
51517     /**
51518      * Selects a cell.
51519      * @param {Number} rowIndex
51520      * @param {Number} collIndex
51521      */
51522     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51523         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51524             this.clearSelections();
51525             r = r || this.grid.dataSource.getAt(rowIndex);
51526             this.selection = {
51527                 record : r,
51528                 cell : [rowIndex, colIndex]
51529             };
51530             if(!preventViewNotify){
51531                 var v = this.grid.getView();
51532                 v.onCellSelect(rowIndex, colIndex);
51533                 if(preventFocus !== true){
51534                     v.focusCell(rowIndex, colIndex);
51535                 }
51536             }
51537             this.fireEvent("cellselect", this, rowIndex, colIndex);
51538             this.fireEvent("selectionchange", this, this.selection);
51539         }
51540     },
51541
51542         //private
51543     isSelectable : function(rowIndex, colIndex, cm){
51544         return !cm.isHidden(colIndex);
51545     },
51546
51547     /** @ignore */
51548     handleKeyDown : function(e){
51549         //Roo.log('Cell Sel Model handleKeyDown');
51550         if(!e.isNavKeyPress()){
51551             return;
51552         }
51553         var g = this.grid, s = this.selection;
51554         if(!s){
51555             e.stopEvent();
51556             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51557             if(cell){
51558                 this.select(cell[0], cell[1]);
51559             }
51560             return;
51561         }
51562         var sm = this;
51563         var walk = function(row, col, step){
51564             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51565         };
51566         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51567         var newCell;
51568
51569       
51570
51571         switch(k){
51572             case e.TAB:
51573                 // handled by onEditorKey
51574                 if (g.isEditor && g.editing) {
51575                     return;
51576                 }
51577                 if(e.shiftKey) {
51578                     newCell = walk(r, c-1, -1);
51579                 } else {
51580                     newCell = walk(r, c+1, 1);
51581                 }
51582                 break;
51583             
51584             case e.DOWN:
51585                newCell = walk(r+1, c, 1);
51586                 break;
51587             
51588             case e.UP:
51589                 newCell = walk(r-1, c, -1);
51590                 break;
51591             
51592             case e.RIGHT:
51593                 newCell = walk(r, c+1, 1);
51594                 break;
51595             
51596             case e.LEFT:
51597                 newCell = walk(r, c-1, -1);
51598                 break;
51599             
51600             case e.ENTER:
51601                 
51602                 if(g.isEditor && !g.editing){
51603                    g.startEditing(r, c);
51604                    e.stopEvent();
51605                    return;
51606                 }
51607                 
51608                 
51609              break;
51610         };
51611         if(newCell){
51612             this.select(newCell[0], newCell[1]);
51613             e.stopEvent();
51614             
51615         }
51616     },
51617
51618     acceptsNav : function(row, col, cm){
51619         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51620     },
51621     /**
51622      * Selects a cell.
51623      * @param {Number} field (not used) - as it's normally used as a listener
51624      * @param {Number} e - event - fake it by using
51625      *
51626      * var e = Roo.EventObjectImpl.prototype;
51627      * e.keyCode = e.TAB
51628      *
51629      * 
51630      */
51631     onEditorKey : function(field, e){
51632         
51633         var k = e.getKey(),
51634             newCell,
51635             g = this.grid,
51636             ed = g.activeEditor,
51637             forward = false;
51638         ///Roo.log('onEditorKey' + k);
51639         
51640         
51641         if (this.enter_is_tab && k == e.ENTER) {
51642             k = e.TAB;
51643         }
51644         
51645         if(k == e.TAB){
51646             if(e.shiftKey){
51647                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51648             }else{
51649                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51650                 forward = true;
51651             }
51652             
51653             e.stopEvent();
51654             
51655         } else if(k == e.ENTER &&  !e.ctrlKey){
51656             ed.completeEdit();
51657             e.stopEvent();
51658             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51659         
51660                 } else if(k == e.ESC){
51661             ed.cancelEdit();
51662         }
51663                 
51664         if (newCell) {
51665             var ecall = { cell : newCell, forward : forward };
51666             this.fireEvent('beforeeditnext', ecall );
51667             newCell = ecall.cell;
51668                         forward = ecall.forward;
51669         }
51670                 
51671         if(newCell){
51672             //Roo.log('next cell after edit');
51673             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51674         } else if (forward) {
51675             // tabbed past last
51676             this.fireEvent.defer(100, this, ['tabend',this]);
51677         }
51678     }
51679 });/*
51680  * Based on:
51681  * Ext JS Library 1.1.1
51682  * Copyright(c) 2006-2007, Ext JS, LLC.
51683  *
51684  * Originally Released Under LGPL - original licence link has changed is not relivant.
51685  *
51686  * Fork - LGPL
51687  * <script type="text/javascript">
51688  */
51689  
51690 /**
51691  * @class Roo.grid.EditorGrid
51692  * @extends Roo.grid.Grid
51693  * Class for creating and editable grid.
51694  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51695  * The container MUST have some type of size defined for the grid to fill. The container will be 
51696  * automatically set to position relative if it isn't already.
51697  * @param {Object} dataSource The data model to bind to
51698  * @param {Object} colModel The column model with info about this grid's columns
51699  */
51700 Roo.grid.EditorGrid = function(container, config){
51701     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51702     this.getGridEl().addClass("xedit-grid");
51703
51704     if(!this.selModel){
51705         this.selModel = new Roo.grid.CellSelectionModel();
51706     }
51707
51708     this.activeEditor = null;
51709
51710         this.addEvents({
51711             /**
51712              * @event beforeedit
51713              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51714              * <ul style="padding:5px;padding-left:16px;">
51715              * <li>grid - This grid</li>
51716              * <li>record - The record being edited</li>
51717              * <li>field - The field name being edited</li>
51718              * <li>value - The value for the field being edited.</li>
51719              * <li>row - The grid row index</li>
51720              * <li>column - The grid column index</li>
51721              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51722              * </ul>
51723              * @param {Object} e An edit event (see above for description)
51724              */
51725             "beforeedit" : true,
51726             /**
51727              * @event afteredit
51728              * Fires after a cell is edited. <br />
51729              * <ul style="padding:5px;padding-left:16px;">
51730              * <li>grid - This grid</li>
51731              * <li>record - The record being edited</li>
51732              * <li>field - The field name being edited</li>
51733              * <li>value - The value being set</li>
51734              * <li>originalValue - The original value for the field, before the edit.</li>
51735              * <li>row - The grid row index</li>
51736              * <li>column - The grid column index</li>
51737              * </ul>
51738              * @param {Object} e An edit event (see above for description)
51739              */
51740             "afteredit" : true,
51741             /**
51742              * @event validateedit
51743              * Fires after a cell is edited, but before the value is set in the record. 
51744          * You can use this to modify the value being set in the field, Return false
51745              * to cancel the change. The edit event object has the following properties <br />
51746              * <ul style="padding:5px;padding-left:16px;">
51747          * <li>editor - This editor</li>
51748              * <li>grid - This grid</li>
51749              * <li>record - The record being edited</li>
51750              * <li>field - The field name being edited</li>
51751              * <li>value - The value being set</li>
51752              * <li>originalValue - The original value for the field, before the edit.</li>
51753              * <li>row - The grid row index</li>
51754              * <li>column - The grid column index</li>
51755              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51756              * </ul>
51757              * @param {Object} e An edit event (see above for description)
51758              */
51759             "validateedit" : true
51760         });
51761     this.on("bodyscroll", this.stopEditing,  this);
51762     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51763 };
51764
51765 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51766     /**
51767      * @cfg {Number} clicksToEdit
51768      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51769      */
51770     clicksToEdit: 2,
51771
51772     // private
51773     isEditor : true,
51774     // private
51775     trackMouseOver: false, // causes very odd FF errors
51776
51777     onCellDblClick : function(g, row, col){
51778         this.startEditing(row, col);
51779     },
51780
51781     onEditComplete : function(ed, value, startValue){
51782         this.editing = false;
51783         this.activeEditor = null;
51784         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51785         var r = ed.record;
51786         var field = this.colModel.getDataIndex(ed.col);
51787         var e = {
51788             grid: this,
51789             record: r,
51790             field: field,
51791             originalValue: startValue,
51792             value: value,
51793             row: ed.row,
51794             column: ed.col,
51795             cancel:false,
51796             editor: ed
51797         };
51798         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51799         cell.show();
51800           
51801         if(String(value) !== String(startValue)){
51802             
51803             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51804                 r.set(field, e.value);
51805                 // if we are dealing with a combo box..
51806                 // then we also set the 'name' colum to be the displayField
51807                 if (ed.field.displayField && ed.field.name) {
51808                     r.set(ed.field.name, ed.field.el.dom.value);
51809                 }
51810                 
51811                 delete e.cancel; //?? why!!!
51812                 this.fireEvent("afteredit", e);
51813             }
51814         } else {
51815             this.fireEvent("afteredit", e); // always fire it!
51816         }
51817         this.view.focusCell(ed.row, ed.col);
51818     },
51819
51820     /**
51821      * Starts editing the specified for the specified row/column
51822      * @param {Number} rowIndex
51823      * @param {Number} colIndex
51824      */
51825     startEditing : function(row, col){
51826         this.stopEditing();
51827         if(this.colModel.isCellEditable(col, row)){
51828             this.view.ensureVisible(row, col, true);
51829           
51830             var r = this.dataSource.getAt(row);
51831             var field = this.colModel.getDataIndex(col);
51832             var cell = Roo.get(this.view.getCell(row,col));
51833             var e = {
51834                 grid: this,
51835                 record: r,
51836                 field: field,
51837                 value: r.data[field],
51838                 row: row,
51839                 column: col,
51840                 cancel:false 
51841             };
51842             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51843                 this.editing = true;
51844                 var ed = this.colModel.getCellEditor(col, row);
51845                 
51846                 if (!ed) {
51847                     return;
51848                 }
51849                 if(!ed.rendered){
51850                     ed.render(ed.parentEl || document.body);
51851                 }
51852                 ed.field.reset();
51853                
51854                 cell.hide();
51855                 
51856                 (function(){ // complex but required for focus issues in safari, ie and opera
51857                     ed.row = row;
51858                     ed.col = col;
51859                     ed.record = r;
51860                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51861                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51862                     this.activeEditor = ed;
51863                     var v = r.data[field];
51864                     ed.startEdit(this.view.getCell(row, col), v);
51865                     // combo's with 'displayField and name set
51866                     if (ed.field.displayField && ed.field.name) {
51867                         ed.field.el.dom.value = r.data[ed.field.name];
51868                     }
51869                     
51870                     
51871                 }).defer(50, this);
51872             }
51873         }
51874     },
51875         
51876     /**
51877      * Stops any active editing
51878      */
51879     stopEditing : function(){
51880         if(this.activeEditor){
51881             this.activeEditor.completeEdit();
51882         }
51883         this.activeEditor = null;
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 // private - not really -- you end up using it !
51897 // This is a support class used internally by the Grid components
51898
51899 /**
51900  * @class Roo.grid.GridEditor
51901  * @extends Roo.Editor
51902  * Class for creating and editable grid elements.
51903  * @param {Object} config any settings (must include field)
51904  */
51905 Roo.grid.GridEditor = function(field, config){
51906     if (!config && field.field) {
51907         config = field;
51908         field = Roo.factory(config.field, Roo.form);
51909     }
51910     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51911     field.monitorTab = false;
51912 };
51913
51914 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51915     
51916     /**
51917      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51918      */
51919     
51920     alignment: "tl-tl",
51921     autoSize: "width",
51922     hideEl : false,
51923     cls: "x-small-editor x-grid-editor",
51924     shim:false,
51925     shadow:"frame"
51926 });/*
51927  * Based on:
51928  * Ext JS Library 1.1.1
51929  * Copyright(c) 2006-2007, Ext JS, LLC.
51930  *
51931  * Originally Released Under LGPL - original licence link has changed is not relivant.
51932  *
51933  * Fork - LGPL
51934  * <script type="text/javascript">
51935  */
51936   
51937
51938   
51939 Roo.grid.PropertyRecord = Roo.data.Record.create([
51940     {name:'name',type:'string'},  'value'
51941 ]);
51942
51943
51944 Roo.grid.PropertyStore = function(grid, source){
51945     this.grid = grid;
51946     this.store = new Roo.data.Store({
51947         recordType : Roo.grid.PropertyRecord
51948     });
51949     this.store.on('update', this.onUpdate,  this);
51950     if(source){
51951         this.setSource(source);
51952     }
51953     Roo.grid.PropertyStore.superclass.constructor.call(this);
51954 };
51955
51956
51957
51958 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51959     setSource : function(o){
51960         this.source = o;
51961         this.store.removeAll();
51962         var data = [];
51963         for(var k in o){
51964             if(this.isEditableValue(o[k])){
51965                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51966             }
51967         }
51968         this.store.loadRecords({records: data}, {}, true);
51969     },
51970
51971     onUpdate : function(ds, record, type){
51972         if(type == Roo.data.Record.EDIT){
51973             var v = record.data['value'];
51974             var oldValue = record.modified['value'];
51975             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51976                 this.source[record.id] = v;
51977                 record.commit();
51978                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51979             }else{
51980                 record.reject();
51981             }
51982         }
51983     },
51984
51985     getProperty : function(row){
51986        return this.store.getAt(row);
51987     },
51988
51989     isEditableValue: function(val){
51990         if(val && val instanceof Date){
51991             return true;
51992         }else if(typeof val == 'object' || typeof val == 'function'){
51993             return false;
51994         }
51995         return true;
51996     },
51997
51998     setValue : function(prop, value){
51999         this.source[prop] = value;
52000         this.store.getById(prop).set('value', value);
52001     },
52002
52003     getSource : function(){
52004         return this.source;
52005     }
52006 });
52007
52008 Roo.grid.PropertyColumnModel = function(grid, store){
52009     this.grid = grid;
52010     var g = Roo.grid;
52011     g.PropertyColumnModel.superclass.constructor.call(this, [
52012         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
52013         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
52014     ]);
52015     this.store = store;
52016     this.bselect = Roo.DomHelper.append(document.body, {
52017         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
52018             {tag: 'option', value: 'true', html: 'true'},
52019             {tag: 'option', value: 'false', html: 'false'}
52020         ]
52021     });
52022     Roo.id(this.bselect);
52023     var f = Roo.form;
52024     this.editors = {
52025         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
52026         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
52027         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
52028         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
52029         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
52030     };
52031     this.renderCellDelegate = this.renderCell.createDelegate(this);
52032     this.renderPropDelegate = this.renderProp.createDelegate(this);
52033 };
52034
52035 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
52036     
52037     
52038     nameText : 'Name',
52039     valueText : 'Value',
52040     
52041     dateFormat : 'm/j/Y',
52042     
52043     
52044     renderDate : function(dateVal){
52045         return dateVal.dateFormat(this.dateFormat);
52046     },
52047
52048     renderBool : function(bVal){
52049         return bVal ? 'true' : 'false';
52050     },
52051
52052     isCellEditable : function(colIndex, rowIndex){
52053         return colIndex == 1;
52054     },
52055
52056     getRenderer : function(col){
52057         return col == 1 ?
52058             this.renderCellDelegate : this.renderPropDelegate;
52059     },
52060
52061     renderProp : function(v){
52062         return this.getPropertyName(v);
52063     },
52064
52065     renderCell : function(val){
52066         var rv = val;
52067         if(val instanceof Date){
52068             rv = this.renderDate(val);
52069         }else if(typeof val == 'boolean'){
52070             rv = this.renderBool(val);
52071         }
52072         return Roo.util.Format.htmlEncode(rv);
52073     },
52074
52075     getPropertyName : function(name){
52076         var pn = this.grid.propertyNames;
52077         return pn && pn[name] ? pn[name] : name;
52078     },
52079
52080     getCellEditor : function(colIndex, rowIndex){
52081         var p = this.store.getProperty(rowIndex);
52082         var n = p.data['name'], val = p.data['value'];
52083         
52084         if(typeof(this.grid.customEditors[n]) == 'string'){
52085             return this.editors[this.grid.customEditors[n]];
52086         }
52087         if(typeof(this.grid.customEditors[n]) != 'undefined'){
52088             return this.grid.customEditors[n];
52089         }
52090         if(val instanceof Date){
52091             return this.editors['date'];
52092         }else if(typeof val == 'number'){
52093             return this.editors['number'];
52094         }else if(typeof val == 'boolean'){
52095             return this.editors['boolean'];
52096         }else{
52097             return this.editors['string'];
52098         }
52099     }
52100 });
52101
52102 /**
52103  * @class Roo.grid.PropertyGrid
52104  * @extends Roo.grid.EditorGrid
52105  * This class represents the  interface of a component based property grid control.
52106  * <br><br>Usage:<pre><code>
52107  var grid = new Roo.grid.PropertyGrid("my-container-id", {
52108       
52109  });
52110  // set any options
52111  grid.render();
52112  * </code></pre>
52113   
52114  * @constructor
52115  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52116  * The container MUST have some type of size defined for the grid to fill. The container will be
52117  * automatically set to position relative if it isn't already.
52118  * @param {Object} config A config object that sets properties on this grid.
52119  */
52120 Roo.grid.PropertyGrid = function(container, config){
52121     config = config || {};
52122     var store = new Roo.grid.PropertyStore(this);
52123     this.store = store;
52124     var cm = new Roo.grid.PropertyColumnModel(this, store);
52125     store.store.sort('name', 'ASC');
52126     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
52127         ds: store.store,
52128         cm: cm,
52129         enableColLock:false,
52130         enableColumnMove:false,
52131         stripeRows:false,
52132         trackMouseOver: false,
52133         clicksToEdit:1
52134     }, config));
52135     this.getGridEl().addClass('x-props-grid');
52136     this.lastEditRow = null;
52137     this.on('columnresize', this.onColumnResize, this);
52138     this.addEvents({
52139          /**
52140              * @event beforepropertychange
52141              * Fires before a property changes (return false to stop?)
52142              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52143              * @param {String} id Record Id
52144              * @param {String} newval New Value
52145          * @param {String} oldval Old Value
52146              */
52147         "beforepropertychange": true,
52148         /**
52149              * @event propertychange
52150              * Fires after a property changes
52151              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52152              * @param {String} id Record Id
52153              * @param {String} newval New Value
52154          * @param {String} oldval Old Value
52155              */
52156         "propertychange": true
52157     });
52158     this.customEditors = this.customEditors || {};
52159 };
52160 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
52161     
52162      /**
52163      * @cfg {Object} customEditors map of colnames=> custom editors.
52164      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
52165      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
52166      * false disables editing of the field.
52167          */
52168     
52169       /**
52170      * @cfg {Object} propertyNames map of property Names to their displayed value
52171          */
52172     
52173     render : function(){
52174         Roo.grid.PropertyGrid.superclass.render.call(this);
52175         this.autoSize.defer(100, this);
52176     },
52177
52178     autoSize : function(){
52179         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52180         if(this.view){
52181             this.view.fitColumns();
52182         }
52183     },
52184
52185     onColumnResize : function(){
52186         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52187         this.autoSize();
52188     },
52189     /**
52190      * Sets the data for the Grid
52191      * accepts a Key => Value object of all the elements avaiable.
52192      * @param {Object} data  to appear in grid.
52193      */
52194     setSource : function(source){
52195         this.store.setSource(source);
52196         //this.autoSize();
52197     },
52198     /**
52199      * Gets all the data from the grid.
52200      * @return {Object} data  data stored in grid
52201      */
52202     getSource : function(){
52203         return this.store.getSource();
52204     }
52205 });/*
52206  * Based on:
52207  * Ext JS Library 1.1.1
52208  * Copyright(c) 2006-2007, Ext JS, LLC.
52209  *
52210  * Originally Released Under LGPL - original licence link has changed is not relivant.
52211  *
52212  * Fork - LGPL
52213  * <script type="text/javascript">
52214  */
52215  
52216 /**
52217  * @class Roo.LoadMask
52218  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52219  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52220  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52221  * element's UpdateManager load indicator and will be destroyed after the initial load.
52222  * @constructor
52223  * Create a new LoadMask
52224  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52225  * @param {Object} config The config object
52226  */
52227 Roo.LoadMask = function(el, config){
52228     this.el = Roo.get(el);
52229     Roo.apply(this, config);
52230     if(this.store){
52231         this.store.on('beforeload', this.onBeforeLoad, this);
52232         this.store.on('load', this.onLoad, this);
52233         this.store.on('loadexception', this.onLoadException, this);
52234         this.removeMask = false;
52235     }else{
52236         var um = this.el.getUpdateManager();
52237         um.showLoadIndicator = false; // disable the default indicator
52238         um.on('beforeupdate', this.onBeforeLoad, this);
52239         um.on('update', this.onLoad, this);
52240         um.on('failure', this.onLoad, this);
52241         this.removeMask = true;
52242     }
52243 };
52244
52245 Roo.LoadMask.prototype = {
52246     /**
52247      * @cfg {Boolean} removeMask
52248      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52249      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52250      */
52251     /**
52252      * @cfg {String} msg
52253      * The text to display in a centered loading message box (defaults to 'Loading...')
52254      */
52255     msg : 'Loading...',
52256     /**
52257      * @cfg {String} msgCls
52258      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52259      */
52260     msgCls : 'x-mask-loading',
52261
52262     /**
52263      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52264      * @type Boolean
52265      */
52266     disabled: false,
52267
52268     /**
52269      * Disables the mask to prevent it from being displayed
52270      */
52271     disable : function(){
52272        this.disabled = true;
52273     },
52274
52275     /**
52276      * Enables the mask so that it can be displayed
52277      */
52278     enable : function(){
52279         this.disabled = false;
52280     },
52281     
52282     onLoadException : function()
52283     {
52284         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52285             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52286         }
52287         this.el.unmask(this.removeMask);
52288     },
52289     // private
52290     onLoad : function()
52291     {
52292         this.el.unmask(this.removeMask);
52293     },
52294
52295     // private
52296     onBeforeLoad : function(){
52297         if(!this.disabled){
52298             this.el.mask(this.msg, this.msgCls);
52299         }
52300     },
52301
52302     // private
52303     destroy : function(){
52304         if(this.store){
52305             this.store.un('beforeload', this.onBeforeLoad, this);
52306             this.store.un('load', this.onLoad, this);
52307             this.store.un('loadexception', this.onLoadException, this);
52308         }else{
52309             var um = this.el.getUpdateManager();
52310             um.un('beforeupdate', this.onBeforeLoad, this);
52311             um.un('update', this.onLoad, this);
52312             um.un('failure', this.onLoad, this);
52313         }
52314     }
52315 };/*
52316  * Based on:
52317  * Ext JS Library 1.1.1
52318  * Copyright(c) 2006-2007, Ext JS, LLC.
52319  *
52320  * Originally Released Under LGPL - original licence link has changed is not relivant.
52321  *
52322  * Fork - LGPL
52323  * <script type="text/javascript">
52324  */
52325
52326
52327 /**
52328  * @class Roo.XTemplate
52329  * @extends Roo.Template
52330  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
52331 <pre><code>
52332 var t = new Roo.MasterTemplate(
52333         '&lt;select name="{name}"&gt;',
52334                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
52335         '&lt;/select&gt;'
52336 );
52337  
52338 // then append, applying the master template values
52339  </code></pre>
52340  *
52341  * Supported features:
52342  *
52343  *  Tags:
52344  *    {a_variable} - output encoded.
52345  *    {a_variable.format:("Y-m-d")} - call a method on the variable
52346  *    {a_variable:raw} - unencoded output
52347  *    {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
52348  *    {a_variable:this.method_on_template(...)} - call a method on the template object.
52349  *  
52350  *  Tpl:
52351  *      &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
52352  *      &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
52353  *      &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
52354  *
52355  *      &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
52356  *      &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
52357  *      
52358  *      
52359  */
52360 Roo.XTemplate = function()
52361 {
52362     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52363     if (this.html) {
52364         this.compile();
52365     }
52366 };
52367
52368
52369 Roo.extend(Roo.XTemplate, Roo.Template, {
52370
52371     /**
52372      *
52373      * basic tag replacing syntax
52374      * WORD:WORD()
52375      *
52376      * // you can fake an object call by doing this
52377      *  x.t:(test,tesT) 
52378      * 
52379      */
52380     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52381
52382     
52383     compile: function()
52384     {
52385         var s = this.html;
52386      
52387         s = ['<tpl>', s, '</tpl>'].join('');
52388     
52389         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
52390             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
52391             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
52392             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
52393             m,
52394             id     = 0,
52395             tpls   = [];
52396     
52397         while(true == !!(m = s.match(re))){
52398             var m2   = m[0].match(nameRe),
52399                 m3   = m[0].match(ifRe),
52400                 m4   = m[0].match(execRe),
52401                 exp  = null, 
52402                 fn   = null,
52403                 exec = null,
52404                 name = m2 && m2[1] ? m2[1] : '';
52405                 
52406             if (m3) {
52407                 // if - puts fn into test..
52408                 exp = m3 && m3[1] ? m3[1] : null;
52409                 if(exp){
52410                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52411                 }
52412             }
52413             if (m4) {
52414                 // exec - calls a function... returns empty if true is  returned.
52415                 exp = m4 && m4[1] ? m4[1] : null;
52416                 if(exp){
52417                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52418                 }
52419             }
52420             if (name) {
52421                 // for = 
52422                 switch(name){
52423                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52424                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52425                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52426                 }
52427             }
52428             tpls.push({
52429                 id:     id,
52430                 target: name,
52431                 exec:   exec,
52432                 test:   fn,
52433                 body:   m[1] || ''
52434             });
52435             s = s.replace(m[0], '{xtpl'+ id + '}');
52436             ++id;
52437         }
52438         for(var i = tpls.length-1; i >= 0; --i){
52439             this.compileTpl(tpls[i]);
52440         }
52441         this.master = tpls[tpls.length-1];
52442         this.tpls = tpls;
52443         return this;
52444     },
52445     
52446     applySubTemplate : function(id, values, parent)
52447     {
52448         var t = this.tpls[id];
52449         if(t.test && !t.test.call(this, values, parent)){
52450             return '';
52451         }
52452         if(t.exec && t.exec.call(this, values, parent)){
52453             return '';
52454         }
52455         var vs = t.target ? t.target.call(this, values, parent) : values;
52456         parent = t.target ? values : parent;
52457         if(t.target && vs instanceof Array){
52458             var buf = [];
52459             for(var i = 0, len = vs.length; i < len; i++){
52460                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52461             }
52462             return buf.join('');
52463         }
52464         return t.compiled.call(this, vs, parent);
52465     },
52466
52467     compileTpl : function(tpl)
52468     {
52469         var fm = Roo.util.Format;
52470         var useF = this.disableFormats !== true;
52471         var sep = Roo.isGecko ? "+" : ",";
52472         var fn = function(m, name, format, args){
52473             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
52474             if (typeof(format) == 'undefined') {
52475                 format= 'htmlEncode';
52476             }
52477             if (format == 'raw' ) {
52478                 format = false;
52479             }
52480             
52481             if(name.substr(0, 4) == 'xtpl'){
52482                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52483             }
52484             
52485             var v;
52486             //if(name.indexOf('.') != -1){
52487                 v = name;
52488             //}else{
52489             //    v = "values['" + name + "']";
52490             //}
52491             if(format && useF){
52492                 
52493                 args = args ? ',' + args : "";
52494                  
52495                 if(format.substr(0, 5) != "this."){
52496                     format = "fm." + format + '(';
52497                 }else{
52498                     format = 'this.call("'+ format.substr(5) + '", ';
52499                     args = ", values";
52500                 }
52501                 return "'"+ sep + format + v + args + ")"+sep+"'";
52502             }
52503              
52504             if (args.length) {
52505                 // called with xxyx.yuu:(test,test)
52506                 // change to ()
52507                 return "'"+ sep + "("+v+" === undefined ? '' : " + v + '(' +  args + "))"+sep+"'";
52508             }
52509             // raw.. - :raw modifier..
52510             return "'"+ sep + "("+v+" === undefined ? '' : " + v + ")"+sep+"'";
52511             
52512         };
52513         var body;
52514         // branched to use + in gecko and [].join() in others
52515         if(Roo.isGecko){
52516             body = "tpl.compiled = function(values, parent){ with(values) { return '" +
52517                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52518                     "';};};";
52519         }else{
52520             body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
52521             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52522             body.push("'].join('');};};");
52523             body = body.join('');
52524         }
52525         
52526         /** eval:var:zzzzzzz */
52527         eval(body);
52528         Roo.log(body.replace(/\\n/,'\n'));
52529         
52530         return this;
52531     },
52532
52533     applyTemplate : function(values){
52534         return this.master.compiled.call(this, values, {});
52535         //var s = this.subs;
52536     },
52537
52538     apply : function(){
52539         return this.applyTemplate.apply(this, arguments);
52540     }
52541
52542  });
52543
52544 Roo.XTemplate.from = function(el){
52545     el = Roo.getDom(el);
52546     return new Roo.XTemplate(el.value || el.innerHTML);
52547 };/*
52548  * Original code for Roojs - LGPL
52549  * <script type="text/javascript">
52550  */
52551  
52552 /**
52553  * @class Roo.XComponent
52554  * A delayed Element creator...
52555  * Or a way to group chunks of interface together.
52556  * 
52557  * Mypart.xyx = new Roo.XComponent({
52558
52559     parent : 'Mypart.xyz', // empty == document.element.!!
52560     order : '001',
52561     name : 'xxxx'
52562     region : 'xxxx'
52563     disabled : function() {} 
52564      
52565     tree : function() { // return an tree of xtype declared components
52566         var MODULE = this;
52567         return 
52568         {
52569             xtype : 'NestedLayoutPanel',
52570             // technicall
52571         }
52572      ]
52573  *})
52574  *
52575  *
52576  * It can be used to build a big heiracy, with parent etc.
52577  * or you can just use this to render a single compoent to a dom element
52578  * MYPART.render(Roo.Element | String(id) | dom_element )
52579  * 
52580  * @extends Roo.util.Observable
52581  * @constructor
52582  * @param cfg {Object} configuration of component
52583  * 
52584  */
52585 Roo.XComponent = function(cfg) {
52586     Roo.apply(this, cfg);
52587     this.addEvents({ 
52588         /**
52589              * @event built
52590              * Fires when this the componnt is built
52591              * @param {Roo.XComponent} c the component
52592              */
52593         'built' : true
52594         
52595     });
52596     this.region = this.region || 'center'; // default..
52597     Roo.XComponent.register(this);
52598     this.modules = false;
52599     this.el = false; // where the layout goes..
52600     
52601     
52602 }
52603 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52604     /**
52605      * @property el
52606      * The created element (with Roo.factory())
52607      * @type {Roo.Layout}
52608      */
52609     el  : false,
52610     
52611     /**
52612      * @property el
52613      * for BC  - use el in new code
52614      * @type {Roo.Layout}
52615      */
52616     panel : false,
52617     
52618     /**
52619      * @property layout
52620      * for BC  - use el in new code
52621      * @type {Roo.Layout}
52622      */
52623     layout : false,
52624     
52625      /**
52626      * @cfg {Function|boolean} disabled
52627      * If this module is disabled by some rule, return true from the funtion
52628      */
52629     disabled : false,
52630     
52631     /**
52632      * @cfg {String} parent 
52633      * Name of parent element which it get xtype added to..
52634      */
52635     parent: false,
52636     
52637     /**
52638      * @cfg {String} order
52639      * Used to set the order in which elements are created (usefull for multiple tabs)
52640      */
52641     
52642     order : false,
52643     /**
52644      * @cfg {String} name
52645      * String to display while loading.
52646      */
52647     name : false,
52648     /**
52649      * @cfg {String} region
52650      * Region to render component to (defaults to center)
52651      */
52652     region : 'center',
52653     
52654     /**
52655      * @cfg {Array} items
52656      * A single item array - the first element is the root of the tree..
52657      * It's done this way to stay compatible with the Xtype system...
52658      */
52659     items : false,
52660     
52661     /**
52662      * @property _tree
52663      * The method that retuns the tree of parts that make up this compoennt 
52664      * @type {function}
52665      */
52666     _tree  : false,
52667     
52668      /**
52669      * render
52670      * render element to dom or tree
52671      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52672      */
52673     
52674     render : function(el)
52675     {
52676         
52677         el = el || false;
52678         var hp = this.parent ? 1 : 0;
52679         
52680         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52681             // if parent is a '#.....' string, then let's use that..
52682             var ename = this.parent.substr(1)
52683             this.parent = false;
52684             el = Roo.get(ename);
52685             if (!el) {
52686                 Roo.log("Warning - element can not be found :#" + ename );
52687                 return;
52688             }
52689         }
52690         
52691         
52692         if (!this.parent) {
52693             
52694             el = el ? Roo.get(el) : false;      
52695             
52696             // it's a top level one..
52697             this.parent =  {
52698                 el : new Roo.BorderLayout(el || document.body, {
52699                 
52700                      center: {
52701                          titlebar: false,
52702                          autoScroll:false,
52703                          closeOnTab: true,
52704                          tabPosition: 'top',
52705                           //resizeTabs: true,
52706                          alwaysShowTabs: el && hp? false :  true,
52707                          hideTabs: el || !hp ? true :  false,
52708                          minTabWidth: 140
52709                      }
52710                  })
52711             }
52712         }
52713         
52714                 
52715                 // The 'tree' method is  '_tree now' 
52716             
52717         var tree = this._tree ? this._tree() : this.tree();
52718         tree.region = tree.region || this.region;
52719         this.el = this.parent.el.addxtype(tree);
52720         this.fireEvent('built', this);
52721         
52722         this.panel = this.el;
52723         this.layout = this.panel.layout;
52724                 this.parentLayout = this.parent.layout  || false;  
52725          
52726     }
52727     
52728 });
52729
52730 Roo.apply(Roo.XComponent, {
52731     
52732     /**
52733      * @property  buildCompleted
52734      * True when the builder has completed building the interface.
52735      * @type Boolean
52736      */
52737     buildCompleted : false,
52738      
52739     /**
52740      * @property  topModule
52741      * the upper most module - uses document.element as it's constructor.
52742      * @type Object
52743      */
52744      
52745     topModule  : false,
52746       
52747     /**
52748      * @property  modules
52749      * array of modules to be created by registration system.
52750      * @type {Array} of Roo.XComponent
52751      */
52752     
52753     modules : [],
52754     /**
52755      * @property  elmodules
52756      * array of modules to be created by which use #ID 
52757      * @type {Array} of Roo.XComponent
52758      */
52759      
52760     elmodules : [],
52761
52762     
52763     /**
52764      * Register components to be built later.
52765      *
52766      * This solves the following issues
52767      * - Building is not done on page load, but after an authentication process has occured.
52768      * - Interface elements are registered on page load
52769      * - Parent Interface elements may not be loaded before child, so this handles that..
52770      * 
52771      *
52772      * example:
52773      * 
52774      * MyApp.register({
52775           order : '000001',
52776           module : 'Pman.Tab.projectMgr',
52777           region : 'center',
52778           parent : 'Pman.layout',
52779           disabled : false,  // or use a function..
52780         })
52781      
52782      * * @param {Object} details about module
52783      */
52784     register : function(obj) {
52785                 
52786                 Roo.XComponent.event.fireEvent('register', obj);
52787                 switch(typeof(obj.disabled) ) {
52788                         
52789                         case 'undefined':
52790                                 break;
52791                         
52792                         case 'function':
52793                                 if ( obj.disabled() ) {
52794                                         return;
52795                                 }
52796                                 break;
52797                         default:
52798                                 if (obj.disabled) {
52799                                         return;
52800                                 }
52801                                 break;
52802                 }
52803                 
52804         this.modules.push(obj);
52805          
52806     },
52807     /**
52808      * convert a string to an object..
52809      * eg. 'AAA.BBB' -> finds AAA.BBB
52810
52811      */
52812     
52813     toObject : function(str)
52814     {
52815         if (!str || typeof(str) == 'object') {
52816             return str;
52817         }
52818         if (str.substring(0,1) == '#') {
52819             return str;
52820         }
52821
52822         var ar = str.split('.');
52823         var rt, o;
52824         rt = ar.shift();
52825             /** eval:var:o */
52826         try {
52827             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52828         } catch (e) {
52829             throw "Module not found : " + str;
52830         }
52831         
52832         if (o === false) {
52833             throw "Module not found : " + str;
52834         }
52835         Roo.each(ar, function(e) {
52836             if (typeof(o[e]) == 'undefined') {
52837                 throw "Module not found : " + str;
52838             }
52839             o = o[e];
52840         });
52841         
52842         return o;
52843         
52844     },
52845     
52846     
52847     /**
52848      * move modules into their correct place in the tree..
52849      * 
52850      */
52851     preBuild : function ()
52852     {
52853         var _t = this;
52854         Roo.each(this.modules , function (obj)
52855         {
52856             var opar = obj.parent;
52857             try { 
52858                 obj.parent = this.toObject(opar);
52859             } catch(e) {
52860                 Roo.log("parent:toObject failed: " + e.toString());
52861                 return;
52862             }
52863             
52864             if (!obj.parent) {
52865                                 Roo.debug && Roo.log("GOT top level module");
52866                                 Roo.debug && Roo.log(obj);
52867                                 obj.modules = new Roo.util.MixedCollection(false, 
52868                     function(o) { return o.order + '' }
52869                 );
52870                 this.topModule = obj;
52871                 return;
52872             }
52873                         // parent is a string (usually a dom element name..)
52874             if (typeof(obj.parent) == 'string') {
52875                 this.elmodules.push(obj);
52876                 return;
52877             }
52878             if (obj.parent.constructor != Roo.XComponent) {
52879                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
52880             }
52881             if (!obj.parent.modules) {
52882                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52883                     function(o) { return o.order + '' }
52884                 );
52885             }
52886             
52887             obj.parent.modules.add(obj);
52888         }, this);
52889     },
52890     
52891      /**
52892      * make a list of modules to build.
52893      * @return {Array} list of modules. 
52894      */ 
52895     
52896     buildOrder : function()
52897     {
52898         var _this = this;
52899         var cmp = function(a,b) {   
52900             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52901         };
52902         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52903             throw "No top level modules to build";
52904         }
52905         
52906         // make a flat list in order of modules to build.
52907         var mods = this.topModule ? [ this.topModule ] : [];
52908                 
52909                 // elmodules (is a list of DOM based modules )
52910         Roo.each(this.elmodules,function(e) { mods.push(e) });
52911
52912         
52913         // add modules to their parents..
52914         var addMod = function(m) {
52915                         Roo.debug && Roo.log("build Order: add: " + m.name);
52916             
52917             mods.push(m);
52918             if (m.modules) {
52919                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
52920                 m.modules.keySort('ASC',  cmp );
52921                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
52922
52923                 m.modules.each(addMod);
52924             } else {
52925                                 Roo.debug && Roo.log("build Order: no child modules");
52926                         }
52927             // not sure if this is used any more..
52928             if (m.finalize) {
52929                 m.finalize.name = m.name + " (clean up) ";
52930                 mods.push(m.finalize);
52931             }
52932             
52933         }
52934         if (this.topModule) { 
52935             this.topModule.modules.keySort('ASC',  cmp );
52936             this.topModule.modules.each(addMod);
52937         }
52938         return mods;
52939     },
52940     
52941      /**
52942      * Build the registered modules.
52943      * @param {Object} parent element.
52944      * @param {Function} optional method to call after module has been added.
52945      * 
52946      */ 
52947    
52948     build : function() 
52949     {
52950         
52951         this.preBuild();
52952         var mods = this.buildOrder();
52953       
52954         //this.allmods = mods;
52955         //Roo.debug && Roo.log(mods);
52956         //return;
52957         if (!mods.length) { // should not happen
52958             throw "NO modules!!!";
52959         }
52960         
52961         
52962         var msg = "Building Interface...";
52963         // flash it up as modal - so we store the mask!?
52964         Roo.MessageBox.show({ title: 'loading' });
52965         Roo.MessageBox.show({
52966            title: "Please wait...",
52967            msg: msg,
52968            width:450,
52969            progress:true,
52970            closable:false,
52971            modal: false
52972           
52973         });
52974         var total = mods.length;
52975         
52976         var _this = this;
52977         var progressRun = function() {
52978             if (!mods.length) {
52979                 Roo.debug && Roo.log('hide?');
52980                 Roo.MessageBox.hide();
52981                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
52982                 
52983                 // THE END...
52984                 return false;   
52985             }
52986             
52987             var m = mods.shift();
52988             
52989             
52990             Roo.debug && Roo.log(m);
52991             // not sure if this is supported any more.. - modules that are are just function
52992             if (typeof(m) == 'function') { 
52993                 m.call(this);
52994                 return progressRun.defer(10, _this);
52995             } 
52996             
52997             
52998             msg = "Building Interface " + (total  - mods.length) + 
52999                     " of " + total + 
53000                     (m.name ? (' - ' + m.name) : '');
53001                         Roo.debug && Roo.log(msg);
53002             Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
53003             
53004          
53005             // is the module disabled?
53006             var disabled = (typeof(m.disabled) == 'function') ?
53007                 m.disabled.call(m.module.disabled) : m.disabled;    
53008             
53009             
53010             if (disabled) {
53011                 return progressRun(); // we do not update the display!
53012             }
53013             
53014             // now build 
53015             
53016                         
53017                         
53018             m.render();
53019             // it's 10 on top level, and 1 on others??? why...
53020             return progressRun.defer(10, _this);
53021              
53022         }
53023         progressRun.defer(1, _this);
53024      
53025         
53026         
53027     },
53028         
53029         
53030         /**
53031          * Event Object.
53032          *
53033          *
53034          */
53035         event: false, 
53036     /**
53037          * wrapper for event.on - aliased later..  
53038          * Typically use to register a event handler for register:
53039          *
53040          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
53041          *
53042          */
53043     on : false
53044    
53045     
53046     
53047 });
53048
53049 Roo.XComponent.event = new Roo.util.Observable({
53050                 events : { 
53051                         /**
53052                          * @event register
53053                          * Fires when an Component is registered,
53054                          * set the disable property on the Component to stop registration.
53055                          * @param {Roo.XComponent} c the component being registerd.
53056                          * 
53057                          */
53058                         'register' : true,
53059                         /**
53060                          * @event buildcomplete
53061                          * Fires on the top level element when all elements have been built
53062                          * @param {Roo.XComponent} the top level component.
53063                          */
53064                         'buildcomplete' : true
53065                         
53066                 }
53067 });
53068
53069 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
53070  //<script type="text/javascript">
53071
53072
53073 /**
53074  * @class Roo.Login
53075  * @extends Roo.LayoutDialog
53076  * A generic Login Dialog..... - only one needed in theory!?!?
53077  *
53078  * Fires XComponent builder on success...
53079  * 
53080  * Sends 
53081  *    username,password, lang = for login actions.
53082  *    check = 1 for periodic checking that sesion is valid.
53083  *    passwordRequest = email request password
53084  *    logout = 1 = to logout
53085  * 
53086  * Affects: (this id="????" elements)
53087  *   loading  (removed) (used to indicate application is loading)
53088  *   loading-mask (hides) (used to hide application when it's building loading)
53089  *   
53090  * 
53091  * Usage: 
53092  *    
53093  * 
53094  * Myapp.login = Roo.Login({
53095      url: xxxx,
53096    
53097      realm : 'Myapp', 
53098      
53099      
53100      method : 'POST',
53101      
53102      
53103      * 
53104  })
53105  * 
53106  * 
53107  * 
53108  **/
53109  
53110 Roo.Login = function(cfg)
53111 {
53112     this.addEvents({
53113         'refreshed' : true
53114     });
53115     
53116     Roo.apply(this,cfg);
53117     
53118     Roo.onReady(function() {
53119         this.onLoad();
53120     }, this);
53121     // call parent..
53122     
53123    
53124     Roo.Login.superclass.constructor.call(this, this);
53125     //this.addxtype(this.items[0]);
53126     
53127     
53128 }
53129
53130
53131 Roo.extend(Roo.Login, Roo.LayoutDialog, {
53132     
53133     /**
53134      * @cfg {String} method
53135      * Method used to query for login details.
53136      */
53137     
53138     method : 'POST',
53139     /**
53140      * @cfg {String} url
53141      * URL to query login data. - eg. baseURL + '/Login.php'
53142      */
53143     url : '',
53144     
53145     /**
53146      * @property user
53147      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
53148      * @type {Object} 
53149      */
53150     user : false,
53151     /**
53152      * @property checkFails
53153      * Number of times we have attempted to get authentication check, and failed.
53154      * @type {Number} 
53155      */
53156     checkFails : 0,
53157       /**
53158      * @property intervalID
53159      * The window interval that does the constant login checking.
53160      * @type {Number} 
53161      */
53162     intervalID : 0,
53163     
53164     
53165     onLoad : function() // called on page load...
53166     {
53167         // load 
53168          
53169         if (Roo.get('loading')) { // clear any loading indicator..
53170             Roo.get('loading').remove();
53171         }
53172         
53173         //this.switchLang('en'); // set the language to english..
53174        
53175         this.check({
53176             success:  function(response, opts)  {  // check successfull...
53177             
53178                 var res = this.processResponse(response);
53179                 this.checkFails =0;
53180                 if (!res.success) { // error!
53181                     this.checkFails = 5;
53182                     //console.log('call failure');
53183                     return this.failure(response,opts);
53184                 }
53185                 
53186                 if (!res.data.id) { // id=0 == login failure.
53187                     return this.show();
53188                 }
53189                 
53190                               
53191                         //console.log(success);
53192                 this.fillAuth(res.data);   
53193                 this.checkFails =0;
53194                 Roo.XComponent.build();
53195             },
53196             failure : this.show
53197         });
53198         
53199     }, 
53200     
53201     
53202     check: function(cfg) // called every so often to refresh cookie etc..
53203     {
53204         if (cfg.again) { // could be undefined..
53205             this.checkFails++;
53206         } else {
53207             this.checkFails = 0;
53208         }
53209         var _this = this;
53210         if (this.sending) {
53211             if ( this.checkFails > 4) {
53212                 Roo.MessageBox.alert("Error",  
53213                     "Error getting authentication status. - try reloading, or wait a while", function() {
53214                         _this.sending = false;
53215                     }); 
53216                 return;
53217             }
53218             cfg.again = true;
53219             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
53220             return;
53221         }
53222         this.sending = true;
53223         
53224         Roo.Ajax.request({  
53225             url: this.url,
53226             params: {
53227                 getAuthUser: true
53228             },  
53229             method: this.method,
53230             success:  cfg.success || this.success,
53231             failure : cfg.failure || this.failure,
53232             scope : this,
53233             callCfg : cfg
53234               
53235         });  
53236     }, 
53237     
53238     
53239     logout: function()
53240     {
53241         window.onbeforeunload = function() { }; // false does not work for IE..
53242         this.user = false;
53243         var _this = this;
53244         
53245         Roo.Ajax.request({  
53246             url: this.url,
53247             params: {
53248                 logout: 1
53249             },  
53250             method: 'GET',
53251             failure : function() {
53252                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
53253                     document.location = document.location.toString() + '?ts=' + Math.random();
53254                 });
53255                 
53256             },
53257             success : function() {
53258                 _this.user = false;
53259                 this.checkFails =0;
53260                 // fixme..
53261                 document.location = document.location.toString() + '?ts=' + Math.random();
53262             }
53263               
53264               
53265         }); 
53266     },
53267     
53268     processResponse : function (response)
53269     {
53270         var res = '';
53271         try {
53272             res = Roo.decode(response.responseText);
53273             // oops...
53274             if (typeof(res) != 'object') {
53275                 res = { success : false, errorMsg : res, errors : true };
53276             }
53277             if (typeof(res.success) == 'undefined') {
53278                 res.success = false;
53279             }
53280             
53281         } catch(e) {
53282             res = { success : false,  errorMsg : response.responseText, errors : true };
53283         }
53284         return res;
53285     },
53286     
53287     success : function(response, opts)  // check successfull...
53288     {  
53289         this.sending = false;
53290         var res = this.processResponse(response);
53291         if (!res.success) {
53292             return this.failure(response, opts);
53293         }
53294         if (!res.data || !res.data.id) {
53295             return this.failure(response,opts);
53296         }
53297         //console.log(res);
53298         this.fillAuth(res.data);
53299         
53300         this.checkFails =0;
53301         
53302     },
53303     
53304     
53305     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
53306     {
53307         this.authUser = -1;
53308         this.sending = false;
53309         var res = this.processResponse(response);
53310         //console.log(res);
53311         if ( this.checkFails > 2) {
53312         
53313             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
53314                 "Error getting authentication status. - try reloading"); 
53315             return;
53316         }
53317         opts.callCfg.again = true;
53318         this.check.defer(1000, this, [ opts.callCfg ]);
53319         return;  
53320     },
53321     
53322     
53323     
53324     fillAuth: function(au) {
53325         this.startAuthCheck();
53326         this.authUserId = au.id;
53327         this.authUser = au;
53328         this.lastChecked = new Date();
53329         this.fireEvent('refreshed', au);
53330         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
53331         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
53332         au.lang = au.lang || 'en';
53333         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53334         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53335         this.switchLang(au.lang );
53336         
53337      
53338         // open system... - -on setyp..
53339         if (this.authUserId  < 0) {
53340             Roo.MessageBox.alert("Warning", 
53341                 "This is an open system - please set up a admin user with a password.");  
53342         }
53343          
53344         //Pman.onload(); // which should do nothing if it's a re-auth result...
53345         
53346              
53347     },
53348     
53349     startAuthCheck : function() // starter for timeout checking..
53350     {
53351         if (this.intervalID) { // timer already in place...
53352             return false;
53353         }
53354         var _this = this;
53355         this.intervalID =  window.setInterval(function() {
53356               _this.check(false);
53357             }, 120000); // every 120 secs = 2mins..
53358         
53359         
53360     },
53361          
53362     
53363     switchLang : function (lang) 
53364     {
53365         _T = typeof(_T) == 'undefined' ? false : _T;
53366           if (!_T || !lang.length) {
53367             return;
53368         }
53369         
53370         if (!_T && lang != 'en') {
53371             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53372             return;
53373         }
53374         
53375         if (typeof(_T.en) == 'undefined') {
53376             _T.en = {};
53377             Roo.apply(_T.en, _T);
53378         }
53379         
53380         if (typeof(_T[lang]) == 'undefined') {
53381             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53382             return;
53383         }
53384         
53385         
53386         Roo.apply(_T, _T[lang]);
53387         // just need to set the text values for everything...
53388         var _this = this;
53389         /* this will not work ...
53390         if (this.form) { 
53391             
53392                
53393             function formLabel(name, val) {
53394                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53395             }
53396             
53397             formLabel('password', "Password"+':');
53398             formLabel('username', "Email Address"+':');
53399             formLabel('lang', "Language"+':');
53400             this.dialog.setTitle("Login");
53401             this.dialog.buttons[0].setText("Forgot Password");
53402             this.dialog.buttons[1].setText("Login");
53403         }
53404         */
53405         
53406         
53407     },
53408     
53409     
53410     title: "Login",
53411     modal: true,
53412     width:  350,
53413     //height: 230,
53414     height: 180,
53415     shadow: true,
53416     minWidth:200,
53417     minHeight:180,
53418     //proxyDrag: true,
53419     closable: false,
53420     draggable: false,
53421     collapsible: false,
53422     resizable: false,
53423     center: {  // needed??
53424         autoScroll:false,
53425         titlebar: false,
53426        // tabPosition: 'top',
53427         hideTabs: true,
53428         closeOnTab: true,
53429         alwaysShowTabs: false
53430     } ,
53431     listeners : {
53432         
53433         show  : function(dlg)
53434         {
53435             //console.log(this);
53436             this.form = this.layout.getRegion('center').activePanel.form;
53437             this.form.dialog = dlg;
53438             this.buttons[0].form = this.form;
53439             this.buttons[0].dialog = dlg;
53440             this.buttons[1].form = this.form;
53441             this.buttons[1].dialog = dlg;
53442            
53443            //this.resizeToLogo.defer(1000,this);
53444             // this is all related to resizing for logos..
53445             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53446            //// if (!sz) {
53447              //   this.resizeToLogo.defer(1000,this);
53448              //   return;
53449            // }
53450             //var w = Ext.lib.Dom.getViewWidth() - 100;
53451             //var h = Ext.lib.Dom.getViewHeight() - 100;
53452             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53453             //this.center();
53454             if (this.disabled) {
53455                 this.hide();
53456                 return;
53457             }
53458             
53459             if (this.user.id < 0) { // used for inital setup situations.
53460                 return;
53461             }
53462             
53463             if (this.intervalID) {
53464                 // remove the timer
53465                 window.clearInterval(this.intervalID);
53466                 this.intervalID = false;
53467             }
53468             
53469             
53470             if (Roo.get('loading')) {
53471                 Roo.get('loading').remove();
53472             }
53473             if (Roo.get('loading-mask')) {
53474                 Roo.get('loading-mask').hide();
53475             }
53476             
53477             //incomming._node = tnode;
53478             this.form.reset();
53479             //this.dialog.modal = !modal;
53480             //this.dialog.show();
53481             this.el.unmask(); 
53482             
53483             
53484             this.form.setValues({
53485                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53486                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53487             });
53488             
53489             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53490             if (this.form.findField('username').getValue().length > 0 ){
53491                 this.form.findField('password').focus();
53492             } else {
53493                this.form.findField('username').focus();
53494             }
53495     
53496         }
53497     },
53498     items : [
53499          {
53500        
53501             xtype : 'ContentPanel',
53502             xns : Roo,
53503             region: 'center',
53504             fitToFrame : true,
53505             
53506             items : [
53507     
53508                 {
53509                
53510                     xtype : 'Form',
53511                     xns : Roo.form,
53512                     labelWidth: 100,
53513                     style : 'margin: 10px;',
53514                     
53515                     listeners : {
53516                         actionfailed : function(f, act) {
53517                             // form can return { errors: .... }
53518                                 
53519                             //act.result.errors // invalid form element list...
53520                             //act.result.errorMsg// invalid form element list...
53521                             
53522                             this.dialog.el.unmask();
53523                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53524                                         "Login failed - communication error - try again.");
53525                                       
53526                         },
53527                         actioncomplete: function(re, act) {
53528                              
53529                             Roo.state.Manager.set(
53530                                 this.dialog.realm + '.username',  
53531                                     this.findField('username').getValue()
53532                             );
53533                             Roo.state.Manager.set(
53534                                 this.dialog.realm + '.lang',  
53535                                 this.findField('lang').getValue() 
53536                             );
53537                             
53538                             this.dialog.fillAuth(act.result.data);
53539                               
53540                             this.dialog.hide();
53541                             
53542                             if (Roo.get('loading-mask')) {
53543                                 Roo.get('loading-mask').show();
53544                             }
53545                             Roo.XComponent.build();
53546                             
53547                              
53548                             
53549                         }
53550                     },
53551                     items : [
53552                         {
53553                             xtype : 'TextField',
53554                             xns : Roo.form,
53555                             fieldLabel: "Email Address",
53556                             name: 'username',
53557                             width:200,
53558                             autoCreate : {tag: "input", type: "text", size: "20"}
53559                         },
53560                         {
53561                             xtype : 'TextField',
53562                             xns : Roo.form,
53563                             fieldLabel: "Password",
53564                             inputType: 'password',
53565                             name: 'password',
53566                             width:200,
53567                             autoCreate : {tag: "input", type: "text", size: "20"},
53568                             listeners : {
53569                                 specialkey : function(e,ev) {
53570                                     if (ev.keyCode == 13) {
53571                                         this.form.dialog.el.mask("Logging in");
53572                                         this.form.doAction('submit', {
53573                                             url: this.form.dialog.url,
53574                                             method: this.form.dialog.method
53575                                         });
53576                                     }
53577                                 }
53578                             }  
53579                         },
53580                         {
53581                             xtype : 'ComboBox',
53582                             xns : Roo.form,
53583                             fieldLabel: "Language",
53584                             name : 'langdisp',
53585                             store: {
53586                                 xtype : 'SimpleStore',
53587                                 fields: ['lang', 'ldisp'],
53588                                 data : [
53589                                     [ 'en', 'English' ],
53590                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53591                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53592                                 ]
53593                             },
53594                             
53595                             valueField : 'lang',
53596                             hiddenName:  'lang',
53597                             width: 200,
53598                             displayField:'ldisp',
53599                             typeAhead: false,
53600                             editable: false,
53601                             mode: 'local',
53602                             triggerAction: 'all',
53603                             emptyText:'Select a Language...',
53604                             selectOnFocus:true,
53605                             listeners : {
53606                                 select :  function(cb, rec, ix) {
53607                                     this.form.switchLang(rec.data.lang);
53608                                 }
53609                             }
53610                         
53611                         }
53612                     ]
53613                 }
53614                   
53615                 
53616             ]
53617         }
53618     ],
53619     buttons : [
53620         {
53621             xtype : 'Button',
53622             xns : 'Roo',
53623             text : "Forgot Password",
53624             listeners : {
53625                 click : function() {
53626                     //console.log(this);
53627                     var n = this.form.findField('username').getValue();
53628                     if (!n.length) {
53629                         Roo.MessageBox.alert("Error", "Fill in your email address");
53630                         return;
53631                     }
53632                     Roo.Ajax.request({
53633                         url: this.dialog.url,
53634                         params: {
53635                             passwordRequest: n
53636                         },
53637                         method: this.dialog.method,
53638                         success:  function(response, opts)  {  // check successfull...
53639                         
53640                             var res = this.dialog.processResponse(response);
53641                             if (!res.success) { // error!
53642                                Roo.MessageBox.alert("Error" ,
53643                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53644                                return;
53645                             }
53646                             Roo.MessageBox.alert("Notice" ,
53647                                 "Please check you email for the Password Reset message");
53648                         },
53649                         failure : function() {
53650                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53651                         }
53652                         
53653                     });
53654                 }
53655             }
53656         },
53657         {
53658             xtype : 'Button',
53659             xns : 'Roo',
53660             text : "Login",
53661             listeners : {
53662                 
53663                 click : function () {
53664                         
53665                     this.dialog.el.mask("Logging in");
53666                     this.form.doAction('submit', {
53667                             url: this.dialog.url,
53668                             method: this.dialog.method
53669                     });
53670                 }
53671             }
53672         }
53673     ]
53674   
53675   
53676 })
53677  
53678
53679
53680