roojs-ui.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: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4534 * @constructor
4535 * @param {Object} cfg - Configuration object.
4536 */
4537 Roo.Template = function(cfg){
4538     // BC!
4539     if(cfg instanceof Array){
4540         cfg = cfg.join("");
4541     }else if(arguments.length > 1){
4542         cfg = Array.prototype.join.call(arguments, "");
4543     }
4544     
4545     
4546     if (typeof(cfg) == 'object') {
4547         Roo.apply(this,cfg)
4548     } else {
4549         // bc
4550         this.html = cfg;
4551     }
4552     
4553     
4554 };
4555 Roo.Template.prototype = {
4556     
4557     /**
4558      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4559      */
4560     html : '',
4561     /**
4562      * Returns an HTML fragment of this template with the specified values applied.
4563      * @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'})
4564      * @return {String} The HTML fragment
4565      */
4566     applyTemplate : function(values){
4567         try {
4568             
4569             if(this.compiled){
4570                 return this.compiled(values);
4571             }
4572             var useF = this.disableFormats !== true;
4573             var fm = Roo.util.Format, tpl = this;
4574             var fn = function(m, name, format, args){
4575                 if(format && useF){
4576                     if(format.substr(0, 5) == "this."){
4577                         return tpl.call(format.substr(5), values[name], values);
4578                     }else{
4579                         if(args){
4580                             // quoted values are required for strings in compiled templates, 
4581                             // but for non compiled we need to strip them
4582                             // quoted reversed for jsmin
4583                             var re = /^\s*['"](.*)["']\s*$/;
4584                             args = args.split(',');
4585                             for(var i = 0, len = args.length; i < len; i++){
4586                                 args[i] = args[i].replace(re, "$1");
4587                             }
4588                             args = [values[name]].concat(args);
4589                         }else{
4590                             args = [values[name]];
4591                         }
4592                         return fm[format].apply(fm, args);
4593                     }
4594                 }else{
4595                     return values[name] !== undefined ? values[name] : "";
4596                 }
4597             };
4598             return this.html.replace(this.re, fn);
4599         } catch (e) {
4600             Roo.log(e);
4601             throw e;
4602         }
4603          
4604     },
4605     
4606     /**
4607      * Sets the HTML used as the template and optionally compiles it.
4608      * @param {String} html
4609      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4610      * @return {Roo.Template} this
4611      */
4612     set : function(html, compile){
4613         this.html = html;
4614         this.compiled = null;
4615         if(compile){
4616             this.compile();
4617         }
4618         return this;
4619     },
4620     
4621     /**
4622      * True to disable format functions (defaults to false)
4623      * @type Boolean
4624      */
4625     disableFormats : false,
4626     
4627     /**
4628     * The regular expression used to match template variables 
4629     * @type RegExp
4630     * @property 
4631     */
4632     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4633     
4634     /**
4635      * Compiles the template into an internal function, eliminating the RegEx overhead.
4636      * @return {Roo.Template} this
4637      */
4638     compile : function(){
4639         var fm = Roo.util.Format;
4640         var useF = this.disableFormats !== true;
4641         var sep = Roo.isGecko ? "+" : ",";
4642         var fn = function(m, name, format, args){
4643             if(format && useF){
4644                 args = args ? ',' + args : "";
4645                 if(format.substr(0, 5) != "this."){
4646                     format = "fm." + format + '(';
4647                 }else{
4648                     format = 'this.call("'+ format.substr(5) + '", ';
4649                     args = ", values";
4650                 }
4651             }else{
4652                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4653             }
4654             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4655         };
4656         var body;
4657         // branched to use + in gecko and [].join() in others
4658         if(Roo.isGecko){
4659             body = "this.compiled = function(values){ return '" +
4660                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4661                     "';};";
4662         }else{
4663             body = ["this.compiled = function(values){ return ['"];
4664             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4665             body.push("'].join('');};");
4666             body = body.join('');
4667         }
4668         /**
4669          * eval:var:values
4670          * eval:var:fm
4671          */
4672         eval(body);
4673         return this;
4674     },
4675     
4676     // private function used to call members
4677     call : function(fnName, value, allValues){
4678         return this[fnName](value, allValues);
4679     },
4680     
4681     /**
4682      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4683      * @param {String/HTMLElement/Roo.Element} el The context element
4684      * @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'})
4685      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4686      * @return {HTMLElement/Roo.Element} The new node or Element
4687      */
4688     insertFirst: function(el, values, returnElement){
4689         return this.doInsert('afterBegin', el, values, returnElement);
4690     },
4691
4692     /**
4693      * Applies the supplied values to the template and inserts the new node(s) before el.
4694      * @param {String/HTMLElement/Roo.Element} el The context element
4695      * @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'})
4696      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4697      * @return {HTMLElement/Roo.Element} The new node or Element
4698      */
4699     insertBefore: function(el, values, returnElement){
4700         return this.doInsert('beforeBegin', el, values, returnElement);
4701     },
4702
4703     /**
4704      * Applies the supplied values to the template and inserts the new node(s) after el.
4705      * @param {String/HTMLElement/Roo.Element} el The context element
4706      * @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'})
4707      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4708      * @return {HTMLElement/Roo.Element} The new node or Element
4709      */
4710     insertAfter : function(el, values, returnElement){
4711         return this.doInsert('afterEnd', el, values, returnElement);
4712     },
4713     
4714     /**
4715      * Applies the supplied values to the template and appends the new node(s) to el.
4716      * @param {String/HTMLElement/Roo.Element} el The context element
4717      * @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'})
4718      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4719      * @return {HTMLElement/Roo.Element} The new node or Element
4720      */
4721     append : function(el, values, returnElement){
4722         return this.doInsert('beforeEnd', el, values, returnElement);
4723     },
4724
4725     doInsert : function(where, el, values, returnEl){
4726         el = Roo.getDom(el);
4727         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4728         return returnEl ? Roo.get(newNode, true) : newNode;
4729     },
4730
4731     /**
4732      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
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     overwrite : function(el, values, returnElement){
4739         el = Roo.getDom(el);
4740         el.innerHTML = this.applyTemplate(values);
4741         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4742     }
4743 };
4744 /**
4745  * Alias for {@link #applyTemplate}
4746  * @method
4747  */
4748 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4749
4750 // backwards compat
4751 Roo.DomHelper.Template = Roo.Template;
4752
4753 /**
4754  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4755  * @param {String/HTMLElement} el A DOM element or its id
4756  * @returns {Roo.Template} The created template
4757  * @static
4758  */
4759 Roo.Template.from = function(el){
4760     el = Roo.getDom(el);
4761     return new Roo.Template(el.value || el.innerHTML);
4762 };/*
4763  * Based on:
4764  * Ext JS Library 1.1.1
4765  * Copyright(c) 2006-2007, Ext JS, LLC.
4766  *
4767  * Originally Released Under LGPL - original licence link has changed is not relivant.
4768  *
4769  * Fork - LGPL
4770  * <script type="text/javascript">
4771  */
4772  
4773
4774 /*
4775  * This is code is also distributed under MIT license for use
4776  * with jQuery and prototype JavaScript libraries.
4777  */
4778 /**
4779  * @class Roo.DomQuery
4780 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).
4781 <p>
4782 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>
4783
4784 <p>
4785 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.
4786 </p>
4787 <h4>Element Selectors:</h4>
4788 <ul class="list">
4789     <li> <b>*</b> any element</li>
4790     <li> <b>E</b> an element with the tag E</li>
4791     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4792     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4793     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4794     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4795 </ul>
4796 <h4>Attribute Selectors:</h4>
4797 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4798 <ul class="list">
4799     <li> <b>E[foo]</b> has an attribute "foo"</li>
4800     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4801     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4802     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4803     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4804     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4805     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4806 </ul>
4807 <h4>Pseudo Classes:</h4>
4808 <ul class="list">
4809     <li> <b>E:first-child</b> E is the first child of its parent</li>
4810     <li> <b>E:last-child</b> E is the last child of its parent</li>
4811     <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>
4812     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4813     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4814     <li> <b>E:only-child</b> E is the only child of its parent</li>
4815     <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>
4816     <li> <b>E:first</b> the first E in the resultset</li>
4817     <li> <b>E:last</b> the last E in the resultset</li>
4818     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4819     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4820     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4821     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4822     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4823     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4824     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4825     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4826     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4827 </ul>
4828 <h4>CSS Value Selectors:</h4>
4829 <ul class="list">
4830     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4831     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4832     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4833     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4834     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4835     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4836 </ul>
4837  * @singleton
4838  */
4839 Roo.DomQuery = function(){
4840     var cache = {}, simpleCache = {}, valueCache = {};
4841     var nonSpace = /\S/;
4842     var trimRe = /^\s+|\s+$/g;
4843     var tplRe = /\{(\d+)\}/g;
4844     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4845     var tagTokenRe = /^(#)?([\w-\*]+)/;
4846     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4847
4848     function child(p, index){
4849         var i = 0;
4850         var n = p.firstChild;
4851         while(n){
4852             if(n.nodeType == 1){
4853                if(++i == index){
4854                    return n;
4855                }
4856             }
4857             n = n.nextSibling;
4858         }
4859         return null;
4860     };
4861
4862     function next(n){
4863         while((n = n.nextSibling) && n.nodeType != 1);
4864         return n;
4865     };
4866
4867     function prev(n){
4868         while((n = n.previousSibling) && n.nodeType != 1);
4869         return n;
4870     };
4871
4872     function children(d){
4873         var n = d.firstChild, ni = -1;
4874             while(n){
4875                 var nx = n.nextSibling;
4876                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4877                     d.removeChild(n);
4878                 }else{
4879                     n.nodeIndex = ++ni;
4880                 }
4881                 n = nx;
4882             }
4883             return this;
4884         };
4885
4886     function byClassName(c, a, v){
4887         if(!v){
4888             return c;
4889         }
4890         var r = [], ri = -1, cn;
4891         for(var i = 0, ci; ci = c[i]; i++){
4892             if((' '+ci.className+' ').indexOf(v) != -1){
4893                 r[++ri] = ci;
4894             }
4895         }
4896         return r;
4897     };
4898
4899     function attrValue(n, attr){
4900         if(!n.tagName && typeof n.length != "undefined"){
4901             n = n[0];
4902         }
4903         if(!n){
4904             return null;
4905         }
4906         if(attr == "for"){
4907             return n.htmlFor;
4908         }
4909         if(attr == "class" || attr == "className"){
4910             return n.className;
4911         }
4912         return n.getAttribute(attr) || n[attr];
4913
4914     };
4915
4916     function getNodes(ns, mode, tagName){
4917         var result = [], ri = -1, cs;
4918         if(!ns){
4919             return result;
4920         }
4921         tagName = tagName || "*";
4922         if(typeof ns.getElementsByTagName != "undefined"){
4923             ns = [ns];
4924         }
4925         if(!mode){
4926             for(var i = 0, ni; ni = ns[i]; i++){
4927                 cs = ni.getElementsByTagName(tagName);
4928                 for(var j = 0, ci; ci = cs[j]; j++){
4929                     result[++ri] = ci;
4930                 }
4931             }
4932         }else if(mode == "/" || mode == ">"){
4933             var utag = tagName.toUpperCase();
4934             for(var i = 0, ni, cn; ni = ns[i]; i++){
4935                 cn = ni.children || ni.childNodes;
4936                 for(var j = 0, cj; cj = cn[j]; j++){
4937                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4938                         result[++ri] = cj;
4939                     }
4940                 }
4941             }
4942         }else if(mode == "+"){
4943             var utag = tagName.toUpperCase();
4944             for(var i = 0, n; n = ns[i]; i++){
4945                 while((n = n.nextSibling) && n.nodeType != 1);
4946                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4947                     result[++ri] = n;
4948                 }
4949             }
4950         }else if(mode == "~"){
4951             for(var i = 0, n; n = ns[i]; i++){
4952                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4953                 if(n){
4954                     result[++ri] = n;
4955                 }
4956             }
4957         }
4958         return result;
4959     };
4960
4961     function concat(a, b){
4962         if(b.slice){
4963             return a.concat(b);
4964         }
4965         for(var i = 0, l = b.length; i < l; i++){
4966             a[a.length] = b[i];
4967         }
4968         return a;
4969     }
4970
4971     function byTag(cs, tagName){
4972         if(cs.tagName || cs == document){
4973             cs = [cs];
4974         }
4975         if(!tagName){
4976             return cs;
4977         }
4978         var r = [], ri = -1;
4979         tagName = tagName.toLowerCase();
4980         for(var i = 0, ci; ci = cs[i]; i++){
4981             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4982                 r[++ri] = ci;
4983             }
4984         }
4985         return r;
4986     };
4987
4988     function byId(cs, attr, id){
4989         if(cs.tagName || cs == document){
4990             cs = [cs];
4991         }
4992         if(!id){
4993             return cs;
4994         }
4995         var r = [], ri = -1;
4996         for(var i = 0,ci; ci = cs[i]; i++){
4997             if(ci && ci.id == id){
4998                 r[++ri] = ci;
4999                 return r;
5000             }
5001         }
5002         return r;
5003     };
5004
5005     function byAttribute(cs, attr, value, op, custom){
5006         var r = [], ri = -1, st = custom=="{";
5007         var f = Roo.DomQuery.operators[op];
5008         for(var i = 0, ci; ci = cs[i]; i++){
5009             var a;
5010             if(st){
5011                 a = Roo.DomQuery.getStyle(ci, attr);
5012             }
5013             else if(attr == "class" || attr == "className"){
5014                 a = ci.className;
5015             }else if(attr == "for"){
5016                 a = ci.htmlFor;
5017             }else if(attr == "href"){
5018                 a = ci.getAttribute("href", 2);
5019             }else{
5020                 a = ci.getAttribute(attr);
5021             }
5022             if((f && f(a, value)) || (!f && a)){
5023                 r[++ri] = ci;
5024             }
5025         }
5026         return r;
5027     };
5028
5029     function byPseudo(cs, name, value){
5030         return Roo.DomQuery.pseudos[name](cs, value);
5031     };
5032
5033     // This is for IE MSXML which does not support expandos.
5034     // IE runs the same speed using setAttribute, however FF slows way down
5035     // and Safari completely fails so they need to continue to use expandos.
5036     var isIE = window.ActiveXObject ? true : false;
5037
5038     // this eval is stop the compressor from
5039     // renaming the variable to something shorter
5040     
5041     /** eval:var:batch */
5042     var batch = 30803; 
5043
5044     var key = 30803;
5045
5046     function nodupIEXml(cs){
5047         var d = ++key;
5048         cs[0].setAttribute("_nodup", d);
5049         var r = [cs[0]];
5050         for(var i = 1, len = cs.length; i < len; i++){
5051             var c = cs[i];
5052             if(!c.getAttribute("_nodup") != d){
5053                 c.setAttribute("_nodup", d);
5054                 r[r.length] = c;
5055             }
5056         }
5057         for(var i = 0, len = cs.length; i < len; i++){
5058             cs[i].removeAttribute("_nodup");
5059         }
5060         return r;
5061     }
5062
5063     function nodup(cs){
5064         if(!cs){
5065             return [];
5066         }
5067         var len = cs.length, c, i, r = cs, cj, ri = -1;
5068         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5069             return cs;
5070         }
5071         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5072             return nodupIEXml(cs);
5073         }
5074         var d = ++key;
5075         cs[0]._nodup = d;
5076         for(i = 1; c = cs[i]; i++){
5077             if(c._nodup != d){
5078                 c._nodup = d;
5079             }else{
5080                 r = [];
5081                 for(var j = 0; j < i; j++){
5082                     r[++ri] = cs[j];
5083                 }
5084                 for(j = i+1; cj = cs[j]; j++){
5085                     if(cj._nodup != d){
5086                         cj._nodup = d;
5087                         r[++ri] = cj;
5088                     }
5089                 }
5090                 return r;
5091             }
5092         }
5093         return r;
5094     }
5095
5096     function quickDiffIEXml(c1, c2){
5097         var d = ++key;
5098         for(var i = 0, len = c1.length; i < len; i++){
5099             c1[i].setAttribute("_qdiff", d);
5100         }
5101         var r = [];
5102         for(var i = 0, len = c2.length; i < len; i++){
5103             if(c2[i].getAttribute("_qdiff") != d){
5104                 r[r.length] = c2[i];
5105             }
5106         }
5107         for(var i = 0, len = c1.length; i < len; i++){
5108            c1[i].removeAttribute("_qdiff");
5109         }
5110         return r;
5111     }
5112
5113     function quickDiff(c1, c2){
5114         var len1 = c1.length;
5115         if(!len1){
5116             return c2;
5117         }
5118         if(isIE && c1[0].selectSingleNode){
5119             return quickDiffIEXml(c1, c2);
5120         }
5121         var d = ++key;
5122         for(var i = 0; i < len1; i++){
5123             c1[i]._qdiff = d;
5124         }
5125         var r = [];
5126         for(var i = 0, len = c2.length; i < len; i++){
5127             if(c2[i]._qdiff != d){
5128                 r[r.length] = c2[i];
5129             }
5130         }
5131         return r;
5132     }
5133
5134     function quickId(ns, mode, root, id){
5135         if(ns == root){
5136            var d = root.ownerDocument || root;
5137            return d.getElementById(id);
5138         }
5139         ns = getNodes(ns, mode, "*");
5140         return byId(ns, null, id);
5141     }
5142
5143     return {
5144         getStyle : function(el, name){
5145             return Roo.fly(el).getStyle(name);
5146         },
5147         /**
5148          * Compiles a selector/xpath query into a reusable function. The returned function
5149          * takes one parameter "root" (optional), which is the context node from where the query should start.
5150          * @param {String} selector The selector/xpath query
5151          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5152          * @return {Function}
5153          */
5154         compile : function(path, type){
5155             type = type || "select";
5156             
5157             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5158             var q = path, mode, lq;
5159             var tk = Roo.DomQuery.matchers;
5160             var tklen = tk.length;
5161             var mm;
5162
5163             // accept leading mode switch
5164             var lmode = q.match(modeRe);
5165             if(lmode && lmode[1]){
5166                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5167                 q = q.replace(lmode[1], "");
5168             }
5169             // strip leading slashes
5170             while(path.substr(0, 1)=="/"){
5171                 path = path.substr(1);
5172             }
5173
5174             while(q && lq != q){
5175                 lq = q;
5176                 var tm = q.match(tagTokenRe);
5177                 if(type == "select"){
5178                     if(tm){
5179                         if(tm[1] == "#"){
5180                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5181                         }else{
5182                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5183                         }
5184                         q = q.replace(tm[0], "");
5185                     }else if(q.substr(0, 1) != '@'){
5186                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5187                     }
5188                 }else{
5189                     if(tm){
5190                         if(tm[1] == "#"){
5191                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5192                         }else{
5193                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5194                         }
5195                         q = q.replace(tm[0], "");
5196                     }
5197                 }
5198                 while(!(mm = q.match(modeRe))){
5199                     var matched = false;
5200                     for(var j = 0; j < tklen; j++){
5201                         var t = tk[j];
5202                         var m = q.match(t.re);
5203                         if(m){
5204                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5205                                                     return m[i];
5206                                                 });
5207                             q = q.replace(m[0], "");
5208                             matched = true;
5209                             break;
5210                         }
5211                     }
5212                     // prevent infinite loop on bad selector
5213                     if(!matched){
5214                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5215                     }
5216                 }
5217                 if(mm[1]){
5218                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5219                     q = q.replace(mm[1], "");
5220                 }
5221             }
5222             fn[fn.length] = "return nodup(n);\n}";
5223             
5224              /** 
5225               * list of variables that need from compression as they are used by eval.
5226              *  eval:var:batch 
5227              *  eval:var:nodup
5228              *  eval:var:byTag
5229              *  eval:var:ById
5230              *  eval:var:getNodes
5231              *  eval:var:quickId
5232              *  eval:var:mode
5233              *  eval:var:root
5234              *  eval:var:n
5235              *  eval:var:byClassName
5236              *  eval:var:byPseudo
5237              *  eval:var:byAttribute
5238              *  eval:var:attrValue
5239              * 
5240              **/ 
5241             eval(fn.join(""));
5242             return f;
5243         },
5244
5245         /**
5246          * Selects a group of elements.
5247          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5248          * @param {Node} root (optional) The start of the query (defaults to document).
5249          * @return {Array}
5250          */
5251         select : function(path, root, type){
5252             if(!root || root == document){
5253                 root = document;
5254             }
5255             if(typeof root == "string"){
5256                 root = document.getElementById(root);
5257             }
5258             var paths = path.split(",");
5259             var results = [];
5260             for(var i = 0, len = paths.length; i < len; i++){
5261                 var p = paths[i].replace(trimRe, "");
5262                 if(!cache[p]){
5263                     cache[p] = Roo.DomQuery.compile(p);
5264                     if(!cache[p]){
5265                         throw p + " is not a valid selector";
5266                     }
5267                 }
5268                 var result = cache[p](root);
5269                 if(result && result != document){
5270                     results = results.concat(result);
5271                 }
5272             }
5273             if(paths.length > 1){
5274                 return nodup(results);
5275             }
5276             return results;
5277         },
5278
5279         /**
5280          * Selects a single element.
5281          * @param {String} selector The selector/xpath query
5282          * @param {Node} root (optional) The start of the query (defaults to document).
5283          * @return {Element}
5284          */
5285         selectNode : function(path, root){
5286             return Roo.DomQuery.select(path, root)[0];
5287         },
5288
5289         /**
5290          * Selects the value of a node, optionally replacing null with the defaultValue.
5291          * @param {String} selector The selector/xpath query
5292          * @param {Node} root (optional) The start of the query (defaults to document).
5293          * @param {String} defaultValue
5294          */
5295         selectValue : function(path, root, defaultValue){
5296             path = path.replace(trimRe, "");
5297             if(!valueCache[path]){
5298                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5299             }
5300             var n = valueCache[path](root);
5301             n = n[0] ? n[0] : n;
5302             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5303             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5304         },
5305
5306         /**
5307          * Selects the value of a node, parsing integers and floats.
5308          * @param {String} selector The selector/xpath query
5309          * @param {Node} root (optional) The start of the query (defaults to document).
5310          * @param {Number} defaultValue
5311          * @return {Number}
5312          */
5313         selectNumber : function(path, root, defaultValue){
5314             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5315             return parseFloat(v);
5316         },
5317
5318         /**
5319          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5320          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5321          * @param {String} selector The simple selector to test
5322          * @return {Boolean}
5323          */
5324         is : function(el, ss){
5325             if(typeof el == "string"){
5326                 el = document.getElementById(el);
5327             }
5328             var isArray = (el instanceof Array);
5329             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5330             return isArray ? (result.length == el.length) : (result.length > 0);
5331         },
5332
5333         /**
5334          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5335          * @param {Array} el An array of elements to filter
5336          * @param {String} selector The simple selector to test
5337          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5338          * the selector instead of the ones that match
5339          * @return {Array}
5340          */
5341         filter : function(els, ss, nonMatches){
5342             ss = ss.replace(trimRe, "");
5343             if(!simpleCache[ss]){
5344                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5345             }
5346             var result = simpleCache[ss](els);
5347             return nonMatches ? quickDiff(result, els) : result;
5348         },
5349
5350         /**
5351          * Collection of matching regular expressions and code snippets.
5352          */
5353         matchers : [{
5354                 re: /^\.([\w-]+)/,
5355                 select: 'n = byClassName(n, null, " {1} ");'
5356             }, {
5357                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5358                 select: 'n = byPseudo(n, "{1}", "{2}");'
5359             },{
5360                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5361                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5362             }, {
5363                 re: /^#([\w-]+)/,
5364                 select: 'n = byId(n, null, "{1}");'
5365             },{
5366                 re: /^@([\w-]+)/,
5367                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5368             }
5369         ],
5370
5371         /**
5372          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5373          * 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;.
5374          */
5375         operators : {
5376             "=" : function(a, v){
5377                 return a == v;
5378             },
5379             "!=" : function(a, v){
5380                 return a != v;
5381             },
5382             "^=" : function(a, v){
5383                 return a && a.substr(0, v.length) == v;
5384             },
5385             "$=" : function(a, v){
5386                 return a && a.substr(a.length-v.length) == v;
5387             },
5388             "*=" : function(a, v){
5389                 return a && a.indexOf(v) !== -1;
5390             },
5391             "%=" : function(a, v){
5392                 return (a % v) == 0;
5393             },
5394             "|=" : function(a, v){
5395                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5396             },
5397             "~=" : function(a, v){
5398                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5399             }
5400         },
5401
5402         /**
5403          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5404          * and the argument (if any) supplied in the selector.
5405          */
5406         pseudos : {
5407             "first-child" : function(c){
5408                 var r = [], ri = -1, n;
5409                 for(var i = 0, ci; ci = n = c[i]; i++){
5410                     while((n = n.previousSibling) && n.nodeType != 1);
5411                     if(!n){
5412                         r[++ri] = ci;
5413                     }
5414                 }
5415                 return r;
5416             },
5417
5418             "last-child" : function(c){
5419                 var r = [], ri = -1, n;
5420                 for(var i = 0, ci; ci = n = c[i]; i++){
5421                     while((n = n.nextSibling) && n.nodeType != 1);
5422                     if(!n){
5423                         r[++ri] = ci;
5424                     }
5425                 }
5426                 return r;
5427             },
5428
5429             "nth-child" : function(c, a) {
5430                 var r = [], ri = -1;
5431                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5432                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5433                 for(var i = 0, n; n = c[i]; i++){
5434                     var pn = n.parentNode;
5435                     if (batch != pn._batch) {
5436                         var j = 0;
5437                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5438                             if(cn.nodeType == 1){
5439                                cn.nodeIndex = ++j;
5440                             }
5441                         }
5442                         pn._batch = batch;
5443                     }
5444                     if (f == 1) {
5445                         if (l == 0 || n.nodeIndex == l){
5446                             r[++ri] = n;
5447                         }
5448                     } else if ((n.nodeIndex + l) % f == 0){
5449                         r[++ri] = n;
5450                     }
5451                 }
5452
5453                 return r;
5454             },
5455
5456             "only-child" : function(c){
5457                 var r = [], ri = -1;;
5458                 for(var i = 0, ci; ci = c[i]; i++){
5459                     if(!prev(ci) && !next(ci)){
5460                         r[++ri] = ci;
5461                     }
5462                 }
5463                 return r;
5464             },
5465
5466             "empty" : function(c){
5467                 var r = [], ri = -1;
5468                 for(var i = 0, ci; ci = c[i]; i++){
5469                     var cns = ci.childNodes, j = 0, cn, empty = true;
5470                     while(cn = cns[j]){
5471                         ++j;
5472                         if(cn.nodeType == 1 || cn.nodeType == 3){
5473                             empty = false;
5474                             break;
5475                         }
5476                     }
5477                     if(empty){
5478                         r[++ri] = ci;
5479                     }
5480                 }
5481                 return r;
5482             },
5483
5484             "contains" : function(c, v){
5485                 var r = [], ri = -1;
5486                 for(var i = 0, ci; ci = c[i]; i++){
5487                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5488                         r[++ri] = ci;
5489                     }
5490                 }
5491                 return r;
5492             },
5493
5494             "nodeValue" : function(c, v){
5495                 var r = [], ri = -1;
5496                 for(var i = 0, ci; ci = c[i]; i++){
5497                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5498                         r[++ri] = ci;
5499                     }
5500                 }
5501                 return r;
5502             },
5503
5504             "checked" : function(c){
5505                 var r = [], ri = -1;
5506                 for(var i = 0, ci; ci = c[i]; i++){
5507                     if(ci.checked == true){
5508                         r[++ri] = ci;
5509                     }
5510                 }
5511                 return r;
5512             },
5513
5514             "not" : function(c, ss){
5515                 return Roo.DomQuery.filter(c, ss, true);
5516             },
5517
5518             "odd" : function(c){
5519                 return this["nth-child"](c, "odd");
5520             },
5521
5522             "even" : function(c){
5523                 return this["nth-child"](c, "even");
5524             },
5525
5526             "nth" : function(c, a){
5527                 return c[a-1] || [];
5528             },
5529
5530             "first" : function(c){
5531                 return c[0] || [];
5532             },
5533
5534             "last" : function(c){
5535                 return c[c.length-1] || [];
5536             },
5537
5538             "has" : function(c, ss){
5539                 var s = Roo.DomQuery.select;
5540                 var r = [], ri = -1;
5541                 for(var i = 0, ci; ci = c[i]; i++){
5542                     if(s(ss, ci).length > 0){
5543                         r[++ri] = ci;
5544                     }
5545                 }
5546                 return r;
5547             },
5548
5549             "next" : function(c, ss){
5550                 var is = Roo.DomQuery.is;
5551                 var r = [], ri = -1;
5552                 for(var i = 0, ci; ci = c[i]; i++){
5553                     var n = next(ci);
5554                     if(n && is(n, ss)){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "prev" : function(c, ss){
5562                 var is = Roo.DomQuery.is;
5563                 var r = [], ri = -1;
5564                 for(var i = 0, ci; ci = c[i]; i++){
5565                     var n = prev(ci);
5566                     if(n && is(n, ss)){
5567                         r[++ri] = ci;
5568                     }
5569                 }
5570                 return r;
5571             }
5572         }
5573     };
5574 }();
5575
5576 /**
5577  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5578  * @param {String} path The selector/xpath query
5579  * @param {Node} root (optional) The start of the query (defaults to document).
5580  * @return {Array}
5581  * @member Roo
5582  * @method query
5583  */
5584 Roo.query = Roo.DomQuery.select;
5585 /*
5586  * Based on:
5587  * Ext JS Library 1.1.1
5588  * Copyright(c) 2006-2007, Ext JS, LLC.
5589  *
5590  * Originally Released Under LGPL - original licence link has changed is not relivant.
5591  *
5592  * Fork - LGPL
5593  * <script type="text/javascript">
5594  */
5595
5596 /**
5597  * @class Roo.util.Observable
5598  * Base class that provides a common interface for publishing events. Subclasses are expected to
5599  * to have a property "events" with all the events defined.<br>
5600  * For example:
5601  * <pre><code>
5602  Employee = function(name){
5603     this.name = name;
5604     this.addEvents({
5605         "fired" : true,
5606         "quit" : true
5607     });
5608  }
5609  Roo.extend(Employee, Roo.util.Observable);
5610 </code></pre>
5611  * @param {Object} config properties to use (incuding events / listeners)
5612  */
5613
5614 Roo.util.Observable = function(cfg){
5615     
5616     cfg = cfg|| {};
5617     this.addEvents(cfg.events || {});
5618     if (cfg.events) {
5619         delete cfg.events; // make sure
5620     }
5621      
5622     Roo.apply(this, cfg);
5623     
5624     if(this.listeners){
5625         this.on(this.listeners);
5626         delete this.listeners;
5627     }
5628 };
5629 Roo.util.Observable.prototype = {
5630     /** 
5631  * @cfg {Object} listeners  list of events and functions to call for this object, 
5632  * For example :
5633  * <pre><code>
5634     listeners :  { 
5635        'click' : function(e) {
5636            ..... 
5637         } ,
5638         .... 
5639     } 
5640   </code></pre>
5641  */
5642     
5643     
5644     /**
5645      * Fires the specified event with the passed parameters (minus the event name).
5646      * @param {String} eventName
5647      * @param {Object...} args Variable number of parameters are passed to handlers
5648      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5649      */
5650     fireEvent : function(){
5651         var ce = this.events[arguments[0].toLowerCase()];
5652         if(typeof ce == "object"){
5653             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5654         }else{
5655             return true;
5656         }
5657     },
5658
5659     // private
5660     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5661
5662     /**
5663      * Appends an event handler to this component
5664      * @param {String}   eventName The type of event to listen for
5665      * @param {Function} handler The method the event invokes
5666      * @param {Object}   scope (optional) The scope in which to execute the handler
5667      * function. The handler function's "this" context.
5668      * @param {Object}   options (optional) An object containing handler configuration
5669      * properties. This may contain any of the following properties:<ul>
5670      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5671      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5672      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5673      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5674      * by the specified number of milliseconds. If the event fires again within that time, the original
5675      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5676      * </ul><br>
5677      * <p>
5678      * <b>Combining Options</b><br>
5679      * Using the options argument, it is possible to combine different types of listeners:<br>
5680      * <br>
5681      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5682                 <pre><code>
5683                 el.on('click', this.onClick, this, {
5684                         single: true,
5685                 delay: 100,
5686                 forumId: 4
5687                 });
5688                 </code></pre>
5689      * <p>
5690      * <b>Attaching multiple handlers in 1 call</b><br>
5691      * The method also allows for a single argument to be passed which is a config object containing properties
5692      * which specify multiple handlers.
5693      * <pre><code>
5694                 el.on({
5695                         'click': {
5696                         fn: this.onClick,
5697                         scope: this,
5698                         delay: 100
5699                 }, 
5700                 'mouseover': {
5701                         fn: this.onMouseOver,
5702                         scope: this
5703                 },
5704                 'mouseout': {
5705                         fn: this.onMouseOut,
5706                         scope: this
5707                 }
5708                 });
5709                 </code></pre>
5710      * <p>
5711      * Or a shorthand syntax which passes the same scope object to all handlers:
5712         <pre><code>
5713                 el.on({
5714                         'click': this.onClick,
5715                 'mouseover': this.onMouseOver,
5716                 'mouseout': this.onMouseOut,
5717                 scope: this
5718                 });
5719                 </code></pre>
5720      */
5721     addListener : function(eventName, fn, scope, o){
5722         if(typeof eventName == "object"){
5723             o = eventName;
5724             for(var e in o){
5725                 if(this.filterOptRe.test(e)){
5726                     continue;
5727                 }
5728                 if(typeof o[e] == "function"){
5729                     // shared options
5730                     this.addListener(e, o[e], o.scope,  o);
5731                 }else{
5732                     // individual options
5733                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5734                 }
5735             }
5736             return;
5737         }
5738         o = (!o || typeof o == "boolean") ? {} : o;
5739         eventName = eventName.toLowerCase();
5740         var ce = this.events[eventName] || true;
5741         if(typeof ce == "boolean"){
5742             ce = new Roo.util.Event(this, eventName);
5743             this.events[eventName] = ce;
5744         }
5745         ce.addListener(fn, scope, o);
5746     },
5747
5748     /**
5749      * Removes a listener
5750      * @param {String}   eventName     The type of event to listen for
5751      * @param {Function} handler        The handler to remove
5752      * @param {Object}   scope  (optional) The scope (this object) for the handler
5753      */
5754     removeListener : function(eventName, fn, scope){
5755         var ce = this.events[eventName.toLowerCase()];
5756         if(typeof ce == "object"){
5757             ce.removeListener(fn, scope);
5758         }
5759     },
5760
5761     /**
5762      * Removes all listeners for this object
5763      */
5764     purgeListeners : function(){
5765         for(var evt in this.events){
5766             if(typeof this.events[evt] == "object"){
5767                  this.events[evt].clearListeners();
5768             }
5769         }
5770     },
5771
5772     relayEvents : function(o, events){
5773         var createHandler = function(ename){
5774             return function(){
5775                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5776             };
5777         };
5778         for(var i = 0, len = events.length; i < len; i++){
5779             var ename = events[i];
5780             if(!this.events[ename]){ this.events[ename] = true; };
5781             o.on(ename, createHandler(ename), this);
5782         }
5783     },
5784
5785     /**
5786      * Used to define events on this Observable
5787      * @param {Object} object The object with the events defined
5788      */
5789     addEvents : function(o){
5790         if(!this.events){
5791             this.events = {};
5792         }
5793         Roo.applyIf(this.events, o);
5794     },
5795
5796     /**
5797      * Checks to see if this object has any listeners for a specified event
5798      * @param {String} eventName The name of the event to check for
5799      * @return {Boolean} True if the event is being listened for, else false
5800      */
5801     hasListener : function(eventName){
5802         var e = this.events[eventName];
5803         return typeof e == "object" && e.listeners.length > 0;
5804     }
5805 };
5806 /**
5807  * Appends an event handler to this element (shorthand for addListener)
5808  * @param {String}   eventName     The type of event to listen for
5809  * @param {Function} handler        The method the event invokes
5810  * @param {Object}   scope (optional) The scope in which to execute the handler
5811  * function. The handler function's "this" context.
5812  * @param {Object}   options  (optional)
5813  * @method
5814  */
5815 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5816 /**
5817  * Removes a listener (shorthand for removeListener)
5818  * @param {String}   eventName     The type of event to listen for
5819  * @param {Function} handler        The handler to remove
5820  * @param {Object}   scope  (optional) The scope (this object) for the handler
5821  * @method
5822  */
5823 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5824
5825 /**
5826  * Starts capture on the specified Observable. All events will be passed
5827  * to the supplied function with the event name + standard signature of the event
5828  * <b>before</b> the event is fired. If the supplied function returns false,
5829  * the event will not fire.
5830  * @param {Observable} o The Observable to capture
5831  * @param {Function} fn The function to call
5832  * @param {Object} scope (optional) The scope (this object) for the fn
5833  * @static
5834  */
5835 Roo.util.Observable.capture = function(o, fn, scope){
5836     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5837 };
5838
5839 /**
5840  * Removes <b>all</b> added captures from the Observable.
5841  * @param {Observable} o The Observable to release
5842  * @static
5843  */
5844 Roo.util.Observable.releaseCapture = function(o){
5845     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5846 };
5847
5848 (function(){
5849
5850     var createBuffered = function(h, o, scope){
5851         var task = new Roo.util.DelayedTask();
5852         return function(){
5853             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5854         };
5855     };
5856
5857     var createSingle = function(h, e, fn, scope){
5858         return function(){
5859             e.removeListener(fn, scope);
5860             return h.apply(scope, arguments);
5861         };
5862     };
5863
5864     var createDelayed = function(h, o, scope){
5865         return function(){
5866             var args = Array.prototype.slice.call(arguments, 0);
5867             setTimeout(function(){
5868                 h.apply(scope, args);
5869             }, o.delay || 10);
5870         };
5871     };
5872
5873     Roo.util.Event = function(obj, name){
5874         this.name = name;
5875         this.obj = obj;
5876         this.listeners = [];
5877     };
5878
5879     Roo.util.Event.prototype = {
5880         addListener : function(fn, scope, options){
5881             var o = options || {};
5882             scope = scope || this.obj;
5883             if(!this.isListening(fn, scope)){
5884                 var l = {fn: fn, scope: scope, options: o};
5885                 var h = fn;
5886                 if(o.delay){
5887                     h = createDelayed(h, o, scope);
5888                 }
5889                 if(o.single){
5890                     h = createSingle(h, this, fn, scope);
5891                 }
5892                 if(o.buffer){
5893                     h = createBuffered(h, o, scope);
5894                 }
5895                 l.fireFn = h;
5896                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5897                     this.listeners.push(l);
5898                 }else{
5899                     this.listeners = this.listeners.slice(0);
5900                     this.listeners.push(l);
5901                 }
5902             }
5903         },
5904
5905         findListener : function(fn, scope){
5906             scope = scope || this.obj;
5907             var ls = this.listeners;
5908             for(var i = 0, len = ls.length; i < len; i++){
5909                 var l = ls[i];
5910                 if(l.fn == fn && l.scope == scope){
5911                     return i;
5912                 }
5913             }
5914             return -1;
5915         },
5916
5917         isListening : function(fn, scope){
5918             return this.findListener(fn, scope) != -1;
5919         },
5920
5921         removeListener : function(fn, scope){
5922             var index;
5923             if((index = this.findListener(fn, scope)) != -1){
5924                 if(!this.firing){
5925                     this.listeners.splice(index, 1);
5926                 }else{
5927                     this.listeners = this.listeners.slice(0);
5928                     this.listeners.splice(index, 1);
5929                 }
5930                 return true;
5931             }
5932             return false;
5933         },
5934
5935         clearListeners : function(){
5936             this.listeners = [];
5937         },
5938
5939         fire : function(){
5940             var ls = this.listeners, scope, len = ls.length;
5941             if(len > 0){
5942                 this.firing = true;
5943                 var args = Array.prototype.slice.call(arguments, 0);
5944                 for(var i = 0; i < len; i++){
5945                     var l = ls[i];
5946                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5947                         this.firing = false;
5948                         return false;
5949                     }
5950                 }
5951                 this.firing = false;
5952             }
5953             return true;
5954         }
5955     };
5956 })();/*
5957  * Based on:
5958  * Ext JS Library 1.1.1
5959  * Copyright(c) 2006-2007, Ext JS, LLC.
5960  *
5961  * Originally Released Under LGPL - original licence link has changed is not relivant.
5962  *
5963  * Fork - LGPL
5964  * <script type="text/javascript">
5965  */
5966
5967 /**
5968  * @class Roo.EventManager
5969  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5970  * several useful events directly.
5971  * See {@link Roo.EventObject} for more details on normalized event objects.
5972  * @singleton
5973  */
5974 Roo.EventManager = function(){
5975     var docReadyEvent, docReadyProcId, docReadyState = false;
5976     var resizeEvent, resizeTask, textEvent, textSize;
5977     var E = Roo.lib.Event;
5978     var D = Roo.lib.Dom;
5979
5980
5981     var fireDocReady = function(){
5982         if(!docReadyState){
5983             docReadyState = true;
5984             Roo.isReady = true;
5985             if(docReadyProcId){
5986                 clearInterval(docReadyProcId);
5987             }
5988             if(Roo.isGecko || Roo.isOpera) {
5989                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5990             }
5991             if(Roo.isIE){
5992                 var defer = document.getElementById("ie-deferred-loader");
5993                 if(defer){
5994                     defer.onreadystatechange = null;
5995                     defer.parentNode.removeChild(defer);
5996                 }
5997             }
5998             if(docReadyEvent){
5999                 docReadyEvent.fire();
6000                 docReadyEvent.clearListeners();
6001             }
6002         }
6003     };
6004     
6005     var initDocReady = function(){
6006         docReadyEvent = new Roo.util.Event();
6007         if(Roo.isGecko || Roo.isOpera) {
6008             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6009         }else if(Roo.isIE){
6010             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6011             var defer = document.getElementById("ie-deferred-loader");
6012             defer.onreadystatechange = function(){
6013                 if(this.readyState == "complete"){
6014                     fireDocReady();
6015                 }
6016             };
6017         }else if(Roo.isSafari){ 
6018             docReadyProcId = setInterval(function(){
6019                 var rs = document.readyState;
6020                 if(rs == "complete") {
6021                     fireDocReady();     
6022                  }
6023             }, 10);
6024         }
6025         // no matter what, make sure it fires on load
6026         E.on(window, "load", fireDocReady);
6027     };
6028
6029     var createBuffered = function(h, o){
6030         var task = new Roo.util.DelayedTask(h);
6031         return function(e){
6032             // create new event object impl so new events don't wipe out properties
6033             e = new Roo.EventObjectImpl(e);
6034             task.delay(o.buffer, h, null, [e]);
6035         };
6036     };
6037
6038     var createSingle = function(h, el, ename, fn){
6039         return function(e){
6040             Roo.EventManager.removeListener(el, ename, fn);
6041             h(e);
6042         };
6043     };
6044
6045     var createDelayed = function(h, o){
6046         return function(e){
6047             // create new event object impl so new events don't wipe out properties
6048             e = new Roo.EventObjectImpl(e);
6049             setTimeout(function(){
6050                 h(e);
6051             }, o.delay || 10);
6052         };
6053     };
6054
6055     var listen = function(element, ename, opt, fn, scope){
6056         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6057         fn = fn || o.fn; scope = scope || o.scope;
6058         var el = Roo.getDom(element);
6059         if(!el){
6060             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6061         }
6062         var h = function(e){
6063             e = Roo.EventObject.setEvent(e);
6064             var t;
6065             if(o.delegate){
6066                 t = e.getTarget(o.delegate, el);
6067                 if(!t){
6068                     return;
6069                 }
6070             }else{
6071                 t = e.target;
6072             }
6073             if(o.stopEvent === true){
6074                 e.stopEvent();
6075             }
6076             if(o.preventDefault === true){
6077                e.preventDefault();
6078             }
6079             if(o.stopPropagation === true){
6080                 e.stopPropagation();
6081             }
6082
6083             if(o.normalized === false){
6084                 e = e.browserEvent;
6085             }
6086
6087             fn.call(scope || el, e, t, o);
6088         };
6089         if(o.delay){
6090             h = createDelayed(h, o);
6091         }
6092         if(o.single){
6093             h = createSingle(h, el, ename, fn);
6094         }
6095         if(o.buffer){
6096             h = createBuffered(h, o);
6097         }
6098         fn._handlers = fn._handlers || [];
6099         fn._handlers.push([Roo.id(el), ename, h]);
6100
6101         E.on(el, ename, h);
6102         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6103             el.addEventListener("DOMMouseScroll", h, false);
6104             E.on(window, 'unload', function(){
6105                 el.removeEventListener("DOMMouseScroll", h, false);
6106             });
6107         }
6108         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6109             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6110         }
6111         return h;
6112     };
6113
6114     var stopListening = function(el, ename, fn){
6115         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6116         if(hds){
6117             for(var i = 0, len = hds.length; i < len; i++){
6118                 var h = hds[i];
6119                 if(h[0] == id && h[1] == ename){
6120                     hd = h[2];
6121                     hds.splice(i, 1);
6122                     break;
6123                 }
6124             }
6125         }
6126         E.un(el, ename, hd);
6127         el = Roo.getDom(el);
6128         if(ename == "mousewheel" && el.addEventListener){
6129             el.removeEventListener("DOMMouseScroll", hd, false);
6130         }
6131         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6132             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6133         }
6134     };
6135
6136     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6137     
6138     var pub = {
6139         
6140         
6141         /** 
6142          * Fix for doc tools
6143          * @scope Roo.EventManager
6144          */
6145         
6146         
6147         /** 
6148          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6149          * object with a Roo.EventObject
6150          * @param {Function} fn        The method the event invokes
6151          * @param {Object}   scope    An object that becomes the scope of the handler
6152          * @param {boolean}  override If true, the obj passed in becomes
6153          *                             the execution scope of the listener
6154          * @return {Function} The wrapped function
6155          * @deprecated
6156          */
6157         wrap : function(fn, scope, override){
6158             return function(e){
6159                 Roo.EventObject.setEvent(e);
6160                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6161             };
6162         },
6163         
6164         /**
6165      * Appends an event handler to an element (shorthand for addListener)
6166      * @param {String/HTMLElement}   element        The html element or id to assign the
6167      * @param {String}   eventName The type of event to listen for
6168      * @param {Function} handler The method the event invokes
6169      * @param {Object}   scope (optional) The scope in which to execute the handler
6170      * function. The handler function's "this" context.
6171      * @param {Object}   options (optional) An object containing handler configuration
6172      * properties. This may contain any of the following properties:<ul>
6173      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6174      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6175      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6176      * <li>preventDefault {Boolean} True to prevent the default action</li>
6177      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6178      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6179      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6180      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6181      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6182      * by the specified number of milliseconds. If the event fires again within that time, the original
6183      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6184      * </ul><br>
6185      * <p>
6186      * <b>Combining Options</b><br>
6187      * Using the options argument, it is possible to combine different types of listeners:<br>
6188      * <br>
6189      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6190      * Code:<pre><code>
6191 el.on('click', this.onClick, this, {
6192     single: true,
6193     delay: 100,
6194     stopEvent : true,
6195     forumId: 4
6196 });</code></pre>
6197      * <p>
6198      * <b>Attaching multiple handlers in 1 call</b><br>
6199       * The method also allows for a single argument to be passed which is a config object containing properties
6200      * which specify multiple handlers.
6201      * <p>
6202      * Code:<pre><code>
6203 el.on({
6204     'click' : {
6205         fn: this.onClick
6206         scope: this,
6207         delay: 100
6208     },
6209     'mouseover' : {
6210         fn: this.onMouseOver
6211         scope: this
6212     },
6213     'mouseout' : {
6214         fn: this.onMouseOut
6215         scope: this
6216     }
6217 });</code></pre>
6218      * <p>
6219      * Or a shorthand syntax:<br>
6220      * Code:<pre><code>
6221 el.on({
6222     'click' : this.onClick,
6223     'mouseover' : this.onMouseOver,
6224     'mouseout' : this.onMouseOut
6225     scope: this
6226 });</code></pre>
6227      */
6228         addListener : function(element, eventName, fn, scope, options){
6229             if(typeof eventName == "object"){
6230                 var o = eventName;
6231                 for(var e in o){
6232                     if(propRe.test(e)){
6233                         continue;
6234                     }
6235                     if(typeof o[e] == "function"){
6236                         // shared options
6237                         listen(element, e, o, o[e], o.scope);
6238                     }else{
6239                         // individual options
6240                         listen(element, e, o[e]);
6241                     }
6242                 }
6243                 return;
6244             }
6245             return listen(element, eventName, options, fn, scope);
6246         },
6247         
6248         /**
6249          * Removes an event handler
6250          *
6251          * @param {String/HTMLElement}   element        The id or html element to remove the 
6252          *                             event from
6253          * @param {String}   eventName     The type of event
6254          * @param {Function} fn
6255          * @return {Boolean} True if a listener was actually removed
6256          */
6257         removeListener : function(element, eventName, fn){
6258             return stopListening(element, eventName, fn);
6259         },
6260         
6261         /**
6262          * Fires when the document is ready (before onload and before images are loaded). Can be 
6263          * accessed shorthanded Roo.onReady().
6264          * @param {Function} fn        The method the event invokes
6265          * @param {Object}   scope    An  object that becomes the scope of the handler
6266          * @param {boolean}  options
6267          */
6268         onDocumentReady : function(fn, scope, options){
6269             if(docReadyState){ // if it already fired
6270                 docReadyEvent.addListener(fn, scope, options);
6271                 docReadyEvent.fire();
6272                 docReadyEvent.clearListeners();
6273                 return;
6274             }
6275             if(!docReadyEvent){
6276                 initDocReady();
6277             }
6278             docReadyEvent.addListener(fn, scope, options);
6279         },
6280         
6281         /**
6282          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6283          * @param {Function} fn        The method the event invokes
6284          * @param {Object}   scope    An object that becomes the scope of the handler
6285          * @param {boolean}  options
6286          */
6287         onWindowResize : function(fn, scope, options){
6288             if(!resizeEvent){
6289                 resizeEvent = new Roo.util.Event();
6290                 resizeTask = new Roo.util.DelayedTask(function(){
6291                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6292                 });
6293                 E.on(window, "resize", function(){
6294                     if(Roo.isIE){
6295                         resizeTask.delay(50);
6296                     }else{
6297                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6298                     }
6299                 });
6300             }
6301             resizeEvent.addListener(fn, scope, options);
6302         },
6303
6304         /**
6305          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6306          * @param {Function} fn        The method the event invokes
6307          * @param {Object}   scope    An object that becomes the scope of the handler
6308          * @param {boolean}  options
6309          */
6310         onTextResize : function(fn, scope, options){
6311             if(!textEvent){
6312                 textEvent = new Roo.util.Event();
6313                 var textEl = new Roo.Element(document.createElement('div'));
6314                 textEl.dom.className = 'x-text-resize';
6315                 textEl.dom.innerHTML = 'X';
6316                 textEl.appendTo(document.body);
6317                 textSize = textEl.dom.offsetHeight;
6318                 setInterval(function(){
6319                     if(textEl.dom.offsetHeight != textSize){
6320                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6321                     }
6322                 }, this.textResizeInterval);
6323             }
6324             textEvent.addListener(fn, scope, options);
6325         },
6326
6327         /**
6328          * Removes the passed window resize listener.
6329          * @param {Function} fn        The method the event invokes
6330          * @param {Object}   scope    The scope of handler
6331          */
6332         removeResizeListener : function(fn, scope){
6333             if(resizeEvent){
6334                 resizeEvent.removeListener(fn, scope);
6335             }
6336         },
6337
6338         // private
6339         fireResize : function(){
6340             if(resizeEvent){
6341                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6342             }   
6343         },
6344         /**
6345          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6346          */
6347         ieDeferSrc : false,
6348         /**
6349          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6350          */
6351         textResizeInterval : 50
6352     };
6353     
6354     /**
6355      * Fix for doc tools
6356      * @scopeAlias pub=Roo.EventManager
6357      */
6358     
6359      /**
6360      * Appends an event handler to an element (shorthand for addListener)
6361      * @param {String/HTMLElement}   element        The html element or id to assign the
6362      * @param {String}   eventName The type of event to listen for
6363      * @param {Function} handler The method the event invokes
6364      * @param {Object}   scope (optional) The scope in which to execute the handler
6365      * function. The handler function's "this" context.
6366      * @param {Object}   options (optional) An object containing handler configuration
6367      * properties. This may contain any of the following properties:<ul>
6368      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6369      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6370      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6371      * <li>preventDefault {Boolean} True to prevent the default action</li>
6372      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6373      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6374      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6375      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6376      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6377      * by the specified number of milliseconds. If the event fires again within that time, the original
6378      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6379      * </ul><br>
6380      * <p>
6381      * <b>Combining Options</b><br>
6382      * Using the options argument, it is possible to combine different types of listeners:<br>
6383      * <br>
6384      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6385      * Code:<pre><code>
6386 el.on('click', this.onClick, this, {
6387     single: true,
6388     delay: 100,
6389     stopEvent : true,
6390     forumId: 4
6391 });</code></pre>
6392      * <p>
6393      * <b>Attaching multiple handlers in 1 call</b><br>
6394       * The method also allows for a single argument to be passed which is a config object containing properties
6395      * which specify multiple handlers.
6396      * <p>
6397      * Code:<pre><code>
6398 el.on({
6399     'click' : {
6400         fn: this.onClick
6401         scope: this,
6402         delay: 100
6403     },
6404     'mouseover' : {
6405         fn: this.onMouseOver
6406         scope: this
6407     },
6408     'mouseout' : {
6409         fn: this.onMouseOut
6410         scope: this
6411     }
6412 });</code></pre>
6413      * <p>
6414      * Or a shorthand syntax:<br>
6415      * Code:<pre><code>
6416 el.on({
6417     'click' : this.onClick,
6418     'mouseover' : this.onMouseOver,
6419     'mouseout' : this.onMouseOut
6420     scope: this
6421 });</code></pre>
6422      */
6423     pub.on = pub.addListener;
6424     pub.un = pub.removeListener;
6425
6426     pub.stoppedMouseDownEvent = new Roo.util.Event();
6427     return pub;
6428 }();
6429 /**
6430   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6431   * @param {Function} fn        The method the event invokes
6432   * @param {Object}   scope    An  object that becomes the scope of the handler
6433   * @param {boolean}  override If true, the obj passed in becomes
6434   *                             the execution scope of the listener
6435   * @member Roo
6436   * @method onReady
6437  */
6438 Roo.onReady = Roo.EventManager.onDocumentReady;
6439
6440 Roo.onReady(function(){
6441     var bd = Roo.get(document.body);
6442     if(!bd){ return; }
6443
6444     var cls = [
6445             Roo.isIE ? "roo-ie"
6446             : Roo.isGecko ? "roo-gecko"
6447             : Roo.isOpera ? "roo-opera"
6448             : Roo.isSafari ? "roo-safari" : ""];
6449
6450     if(Roo.isMac){
6451         cls.push("roo-mac");
6452     }
6453     if(Roo.isLinux){
6454         cls.push("roo-linux");
6455     }
6456     if(Roo.isBorderBox){
6457         cls.push('roo-border-box');
6458     }
6459     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6460         var p = bd.dom.parentNode;
6461         if(p){
6462             p.className += ' roo-strict';
6463         }
6464     }
6465     bd.addClass(cls.join(' '));
6466 });
6467
6468 /**
6469  * @class Roo.EventObject
6470  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6471  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6472  * Example:
6473  * <pre><code>
6474  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6475     e.preventDefault();
6476     var target = e.getTarget();
6477     ...
6478  }
6479  var myDiv = Roo.get("myDiv");
6480  myDiv.on("click", handleClick);
6481  //or
6482  Roo.EventManager.on("myDiv", 'click', handleClick);
6483  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6484  </code></pre>
6485  * @singleton
6486  */
6487 Roo.EventObject = function(){
6488     
6489     var E = Roo.lib.Event;
6490     
6491     // safari keypress events for special keys return bad keycodes
6492     var safariKeys = {
6493         63234 : 37, // left
6494         63235 : 39, // right
6495         63232 : 38, // up
6496         63233 : 40, // down
6497         63276 : 33, // page up
6498         63277 : 34, // page down
6499         63272 : 46, // delete
6500         63273 : 36, // home
6501         63275 : 35  // end
6502     };
6503
6504     // normalize button clicks
6505     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6506                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6507
6508     Roo.EventObjectImpl = function(e){
6509         if(e){
6510             this.setEvent(e.browserEvent || e);
6511         }
6512     };
6513     Roo.EventObjectImpl.prototype = {
6514         /**
6515          * Used to fix doc tools.
6516          * @scope Roo.EventObject.prototype
6517          */
6518             
6519
6520         
6521         
6522         /** The normal browser event */
6523         browserEvent : null,
6524         /** The button pressed in a mouse event */
6525         button : -1,
6526         /** True if the shift key was down during the event */
6527         shiftKey : false,
6528         /** True if the control key was down during the event */
6529         ctrlKey : false,
6530         /** True if the alt key was down during the event */
6531         altKey : false,
6532
6533         /** Key constant 
6534         * @type Number */
6535         BACKSPACE : 8,
6536         /** Key constant 
6537         * @type Number */
6538         TAB : 9,
6539         /** Key constant 
6540         * @type Number */
6541         RETURN : 13,
6542         /** Key constant 
6543         * @type Number */
6544         ENTER : 13,
6545         /** Key constant 
6546         * @type Number */
6547         SHIFT : 16,
6548         /** Key constant 
6549         * @type Number */
6550         CONTROL : 17,
6551         /** Key constant 
6552         * @type Number */
6553         ESC : 27,
6554         /** Key constant 
6555         * @type Number */
6556         SPACE : 32,
6557         /** Key constant 
6558         * @type Number */
6559         PAGEUP : 33,
6560         /** Key constant 
6561         * @type Number */
6562         PAGEDOWN : 34,
6563         /** Key constant 
6564         * @type Number */
6565         END : 35,
6566         /** Key constant 
6567         * @type Number */
6568         HOME : 36,
6569         /** Key constant 
6570         * @type Number */
6571         LEFT : 37,
6572         /** Key constant 
6573         * @type Number */
6574         UP : 38,
6575         /** Key constant 
6576         * @type Number */
6577         RIGHT : 39,
6578         /** Key constant 
6579         * @type Number */
6580         DOWN : 40,
6581         /** Key constant 
6582         * @type Number */
6583         DELETE : 46,
6584         /** Key constant 
6585         * @type Number */
6586         F5 : 116,
6587
6588            /** @private */
6589         setEvent : function(e){
6590             if(e == this || (e && e.browserEvent)){ // already wrapped
6591                 return e;
6592             }
6593             this.browserEvent = e;
6594             if(e){
6595                 // normalize buttons
6596                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6597                 if(e.type == 'click' && this.button == -1){
6598                     this.button = 0;
6599                 }
6600                 this.type = e.type;
6601                 this.shiftKey = e.shiftKey;
6602                 // mac metaKey behaves like ctrlKey
6603                 this.ctrlKey = e.ctrlKey || e.metaKey;
6604                 this.altKey = e.altKey;
6605                 // in getKey these will be normalized for the mac
6606                 this.keyCode = e.keyCode;
6607                 // keyup warnings on firefox.
6608                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6609                 // cache the target for the delayed and or buffered events
6610                 this.target = E.getTarget(e);
6611                 // same for XY
6612                 this.xy = E.getXY(e);
6613             }else{
6614                 this.button = -1;
6615                 this.shiftKey = false;
6616                 this.ctrlKey = false;
6617                 this.altKey = false;
6618                 this.keyCode = 0;
6619                 this.charCode =0;
6620                 this.target = null;
6621                 this.xy = [0, 0];
6622             }
6623             return this;
6624         },
6625
6626         /**
6627          * Stop the event (preventDefault and stopPropagation)
6628          */
6629         stopEvent : function(){
6630             if(this.browserEvent){
6631                 if(this.browserEvent.type == 'mousedown'){
6632                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6633                 }
6634                 E.stopEvent(this.browserEvent);
6635             }
6636         },
6637
6638         /**
6639          * Prevents the browsers default handling of the event.
6640          */
6641         preventDefault : function(){
6642             if(this.browserEvent){
6643                 E.preventDefault(this.browserEvent);
6644             }
6645         },
6646
6647         /** @private */
6648         isNavKeyPress : function(){
6649             var k = this.keyCode;
6650             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6651             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6652         },
6653
6654         isSpecialKey : function(){
6655             var k = this.keyCode;
6656             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6657             (k == 16) || (k == 17) ||
6658             (k >= 18 && k <= 20) ||
6659             (k >= 33 && k <= 35) ||
6660             (k >= 36 && k <= 39) ||
6661             (k >= 44 && k <= 45);
6662         },
6663         /**
6664          * Cancels bubbling of the event.
6665          */
6666         stopPropagation : function(){
6667             if(this.browserEvent){
6668                 if(this.type == 'mousedown'){
6669                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6670                 }
6671                 E.stopPropagation(this.browserEvent);
6672             }
6673         },
6674
6675         /**
6676          * Gets the key code for the event.
6677          * @return {Number}
6678          */
6679         getCharCode : function(){
6680             return this.charCode || this.keyCode;
6681         },
6682
6683         /**
6684          * Returns a normalized keyCode for the event.
6685          * @return {Number} The key code
6686          */
6687         getKey : function(){
6688             var k = this.keyCode || this.charCode;
6689             return Roo.isSafari ? (safariKeys[k] || k) : k;
6690         },
6691
6692         /**
6693          * Gets the x coordinate of the event.
6694          * @return {Number}
6695          */
6696         getPageX : function(){
6697             return this.xy[0];
6698         },
6699
6700         /**
6701          * Gets the y coordinate of the event.
6702          * @return {Number}
6703          */
6704         getPageY : function(){
6705             return this.xy[1];
6706         },
6707
6708         /**
6709          * Gets the time of the event.
6710          * @return {Number}
6711          */
6712         getTime : function(){
6713             if(this.browserEvent){
6714                 return E.getTime(this.browserEvent);
6715             }
6716             return null;
6717         },
6718
6719         /**
6720          * Gets the page coordinates of the event.
6721          * @return {Array} The xy values like [x, y]
6722          */
6723         getXY : function(){
6724             return this.xy;
6725         },
6726
6727         /**
6728          * Gets the target for the event.
6729          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6730          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6731                 search as a number or element (defaults to 10 || document.body)
6732          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6733          * @return {HTMLelement}
6734          */
6735         getTarget : function(selector, maxDepth, returnEl){
6736             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6737         },
6738         /**
6739          * Gets the related target.
6740          * @return {HTMLElement}
6741          */
6742         getRelatedTarget : function(){
6743             if(this.browserEvent){
6744                 return E.getRelatedTarget(this.browserEvent);
6745             }
6746             return null;
6747         },
6748
6749         /**
6750          * Normalizes mouse wheel delta across browsers
6751          * @return {Number} The delta
6752          */
6753         getWheelDelta : function(){
6754             var e = this.browserEvent;
6755             var delta = 0;
6756             if(e.wheelDelta){ /* IE/Opera. */
6757                 delta = e.wheelDelta/120;
6758             }else if(e.detail){ /* Mozilla case. */
6759                 delta = -e.detail/3;
6760             }
6761             return delta;
6762         },
6763
6764         /**
6765          * Returns true if the control, meta, shift or alt key was pressed during this event.
6766          * @return {Boolean}
6767          */
6768         hasModifier : function(){
6769             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6770         },
6771
6772         /**
6773          * Returns true if the target of this event equals el or is a child of el
6774          * @param {String/HTMLElement/Element} el
6775          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6776          * @return {Boolean}
6777          */
6778         within : function(el, related){
6779             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6780             return t && Roo.fly(el).contains(t);
6781         },
6782
6783         getPoint : function(){
6784             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6785         }
6786     };
6787
6788     return new Roo.EventObjectImpl();
6789 }();
6790             
6791     /*
6792  * Based on:
6793  * Ext JS Library 1.1.1
6794  * Copyright(c) 2006-2007, Ext JS, LLC.
6795  *
6796  * Originally Released Under LGPL - original licence link has changed is not relivant.
6797  *
6798  * Fork - LGPL
6799  * <script type="text/javascript">
6800  */
6801
6802  
6803 // was in Composite Element!??!?!
6804  
6805 (function(){
6806     var D = Roo.lib.Dom;
6807     var E = Roo.lib.Event;
6808     var A = Roo.lib.Anim;
6809
6810     // local style camelizing for speed
6811     var propCache = {};
6812     var camelRe = /(-[a-z])/gi;
6813     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6814     var view = document.defaultView;
6815
6816 /**
6817  * @class Roo.Element
6818  * Represents an Element in the DOM.<br><br>
6819  * Usage:<br>
6820 <pre><code>
6821 var el = Roo.get("my-div");
6822
6823 // or with getEl
6824 var el = getEl("my-div");
6825
6826 // or with a DOM element
6827 var el = Roo.get(myDivElement);
6828 </code></pre>
6829  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6830  * each call instead of constructing a new one.<br><br>
6831  * <b>Animations</b><br />
6832  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6833  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6834 <pre>
6835 Option    Default   Description
6836 --------- --------  ---------------------------------------------
6837 duration  .35       The duration of the animation in seconds
6838 easing    easeOut   The YUI easing method
6839 callback  none      A function to execute when the anim completes
6840 scope     this      The scope (this) of the callback function
6841 </pre>
6842 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6843 * manipulate the animation. Here's an example:
6844 <pre><code>
6845 var el = Roo.get("my-div");
6846
6847 // no animation
6848 el.setWidth(100);
6849
6850 // default animation
6851 el.setWidth(100, true);
6852
6853 // animation with some options set
6854 el.setWidth(100, {
6855     duration: 1,
6856     callback: this.foo,
6857     scope: this
6858 });
6859
6860 // using the "anim" property to get the Anim object
6861 var opt = {
6862     duration: 1,
6863     callback: this.foo,
6864     scope: this
6865 };
6866 el.setWidth(100, opt);
6867 ...
6868 if(opt.anim.isAnimated()){
6869     opt.anim.stop();
6870 }
6871 </code></pre>
6872 * <b> Composite (Collections of) Elements</b><br />
6873  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6874  * @constructor Create a new Element directly.
6875  * @param {String/HTMLElement} element
6876  * @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).
6877  */
6878     Roo.Element = function(element, forceNew){
6879         var dom = typeof element == "string" ?
6880                 document.getElementById(element) : element;
6881         if(!dom){ // invalid id/element
6882             return null;
6883         }
6884         var id = dom.id;
6885         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6886             return Roo.Element.cache[id];
6887         }
6888
6889         /**
6890          * The DOM element
6891          * @type HTMLElement
6892          */
6893         this.dom = dom;
6894
6895         /**
6896          * The DOM element ID
6897          * @type String
6898          */
6899         this.id = id || Roo.id(dom);
6900     };
6901
6902     var El = Roo.Element;
6903
6904     El.prototype = {
6905         /**
6906          * The element's default display mode  (defaults to "")
6907          * @type String
6908          */
6909         originalDisplay : "",
6910
6911         visibilityMode : 1,
6912         /**
6913          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6914          * @type String
6915          */
6916         defaultUnit : "px",
6917         /**
6918          * Sets the element's visibility mode. When setVisible() is called it
6919          * will use this to determine whether to set the visibility or the display property.
6920          * @param visMode Element.VISIBILITY or Element.DISPLAY
6921          * @return {Roo.Element} this
6922          */
6923         setVisibilityMode : function(visMode){
6924             this.visibilityMode = visMode;
6925             return this;
6926         },
6927         /**
6928          * Convenience method for setVisibilityMode(Element.DISPLAY)
6929          * @param {String} display (optional) What to set display to when visible
6930          * @return {Roo.Element} this
6931          */
6932         enableDisplayMode : function(display){
6933             this.setVisibilityMode(El.DISPLAY);
6934             if(typeof display != "undefined") this.originalDisplay = display;
6935             return this;
6936         },
6937
6938         /**
6939          * 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)
6940          * @param {String} selector The simple selector to test
6941          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6942                 search as a number or element (defaults to 10 || document.body)
6943          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6944          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6945          */
6946         findParent : function(simpleSelector, maxDepth, returnEl){
6947             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6948             maxDepth = maxDepth || 50;
6949             if(typeof maxDepth != "number"){
6950                 stopEl = Roo.getDom(maxDepth);
6951                 maxDepth = 10;
6952             }
6953             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6954                 if(dq.is(p, simpleSelector)){
6955                     return returnEl ? Roo.get(p) : p;
6956                 }
6957                 depth++;
6958                 p = p.parentNode;
6959             }
6960             return null;
6961         },
6962
6963
6964         /**
6965          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6966          * @param {String} selector The simple selector to test
6967          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6968                 search as a number or element (defaults to 10 || document.body)
6969          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6970          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6971          */
6972         findParentNode : function(simpleSelector, maxDepth, returnEl){
6973             var p = Roo.fly(this.dom.parentNode, '_internal');
6974             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6975         },
6976
6977         /**
6978          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6979          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6980          * @param {String} selector The simple selector to test
6981          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6982                 search as a number or element (defaults to 10 || document.body)
6983          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6984          */
6985         up : function(simpleSelector, maxDepth){
6986             return this.findParentNode(simpleSelector, maxDepth, true);
6987         },
6988
6989
6990
6991         /**
6992          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6993          * @param {String} selector The simple selector to test
6994          * @return {Boolean} True if this element matches the selector, else false
6995          */
6996         is : function(simpleSelector){
6997             return Roo.DomQuery.is(this.dom, simpleSelector);
6998         },
6999
7000         /**
7001          * Perform animation on this element.
7002          * @param {Object} args The YUI animation control args
7003          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7004          * @param {Function} onComplete (optional) Function to call when animation completes
7005          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7006          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7007          * @return {Roo.Element} this
7008          */
7009         animate : function(args, duration, onComplete, easing, animType){
7010             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7011             return this;
7012         },
7013
7014         /*
7015          * @private Internal animation call
7016          */
7017         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7018             animType = animType || 'run';
7019             opt = opt || {};
7020             var anim = Roo.lib.Anim[animType](
7021                 this.dom, args,
7022                 (opt.duration || defaultDur) || .35,
7023                 (opt.easing || defaultEase) || 'easeOut',
7024                 function(){
7025                     Roo.callback(cb, this);
7026                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7027                 },
7028                 this
7029             );
7030             opt.anim = anim;
7031             return anim;
7032         },
7033
7034         // private legacy anim prep
7035         preanim : function(a, i){
7036             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7037         },
7038
7039         /**
7040          * Removes worthless text nodes
7041          * @param {Boolean} forceReclean (optional) By default the element
7042          * keeps track if it has been cleaned already so
7043          * you can call this over and over. However, if you update the element and
7044          * need to force a reclean, you can pass true.
7045          */
7046         clean : function(forceReclean){
7047             if(this.isCleaned && forceReclean !== true){
7048                 return this;
7049             }
7050             var ns = /\S/;
7051             var d = this.dom, n = d.firstChild, ni = -1;
7052             while(n){
7053                 var nx = n.nextSibling;
7054                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7055                     d.removeChild(n);
7056                 }else{
7057                     n.nodeIndex = ++ni;
7058                 }
7059                 n = nx;
7060             }
7061             this.isCleaned = true;
7062             return this;
7063         },
7064
7065         // private
7066         calcOffsetsTo : function(el){
7067             el = Roo.get(el);
7068             var d = el.dom;
7069             var restorePos = false;
7070             if(el.getStyle('position') == 'static'){
7071                 el.position('relative');
7072                 restorePos = true;
7073             }
7074             var x = 0, y =0;
7075             var op = this.dom;
7076             while(op && op != d && op.tagName != 'HTML'){
7077                 x+= op.offsetLeft;
7078                 y+= op.offsetTop;
7079                 op = op.offsetParent;
7080             }
7081             if(restorePos){
7082                 el.position('static');
7083             }
7084             return [x, y];
7085         },
7086
7087         /**
7088          * Scrolls this element into view within the passed container.
7089          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7090          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7091          * @return {Roo.Element} this
7092          */
7093         scrollIntoView : function(container, hscroll){
7094             var c = Roo.getDom(container) || document.body;
7095             var el = this.dom;
7096
7097             var o = this.calcOffsetsTo(c),
7098                 l = o[0],
7099                 t = o[1],
7100                 b = t+el.offsetHeight,
7101                 r = l+el.offsetWidth;
7102
7103             var ch = c.clientHeight;
7104             var ct = parseInt(c.scrollTop, 10);
7105             var cl = parseInt(c.scrollLeft, 10);
7106             var cb = ct + ch;
7107             var cr = cl + c.clientWidth;
7108
7109             if(t < ct){
7110                 c.scrollTop = t;
7111             }else if(b > cb){
7112                 c.scrollTop = b-ch;
7113             }
7114
7115             if(hscroll !== false){
7116                 if(l < cl){
7117                     c.scrollLeft = l;
7118                 }else if(r > cr){
7119                     c.scrollLeft = r-c.clientWidth;
7120                 }
7121             }
7122             return this;
7123         },
7124
7125         // private
7126         scrollChildIntoView : function(child, hscroll){
7127             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7128         },
7129
7130         /**
7131          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7132          * the new height may not be available immediately.
7133          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7134          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7135          * @param {Function} onComplete (optional) Function to call when animation completes
7136          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7137          * @return {Roo.Element} this
7138          */
7139         autoHeight : function(animate, duration, onComplete, easing){
7140             var oldHeight = this.getHeight();
7141             this.clip();
7142             this.setHeight(1); // force clipping
7143             setTimeout(function(){
7144                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7145                 if(!animate){
7146                     this.setHeight(height);
7147                     this.unclip();
7148                     if(typeof onComplete == "function"){
7149                         onComplete();
7150                     }
7151                 }else{
7152                     this.setHeight(oldHeight); // restore original height
7153                     this.setHeight(height, animate, duration, function(){
7154                         this.unclip();
7155                         if(typeof onComplete == "function") onComplete();
7156                     }.createDelegate(this), easing);
7157                 }
7158             }.createDelegate(this), 0);
7159             return this;
7160         },
7161
7162         /**
7163          * Returns true if this element is an ancestor of the passed element
7164          * @param {HTMLElement/String} el The element to check
7165          * @return {Boolean} True if this element is an ancestor of el, else false
7166          */
7167         contains : function(el){
7168             if(!el){return false;}
7169             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7170         },
7171
7172         /**
7173          * Checks whether the element is currently visible using both visibility and display properties.
7174          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7175          * @return {Boolean} True if the element is currently visible, else false
7176          */
7177         isVisible : function(deep) {
7178             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7179             if(deep !== true || !vis){
7180                 return vis;
7181             }
7182             var p = this.dom.parentNode;
7183             while(p && p.tagName.toLowerCase() != "body"){
7184                 if(!Roo.fly(p, '_isVisible').isVisible()){
7185                     return false;
7186                 }
7187                 p = p.parentNode;
7188             }
7189             return true;
7190         },
7191
7192         /**
7193          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7194          * @param {String} selector The CSS selector
7195          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7196          * @return {CompositeElement/CompositeElementLite} The composite element
7197          */
7198         select : function(selector, unique){
7199             return El.select(selector, unique, this.dom);
7200         },
7201
7202         /**
7203          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7204          * @param {String} selector The CSS selector
7205          * @return {Array} An array of the matched nodes
7206          */
7207         query : function(selector, unique){
7208             return Roo.DomQuery.select(selector, this.dom);
7209         },
7210
7211         /**
7212          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7213          * @param {String} selector The CSS selector
7214          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7215          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7216          */
7217         child : function(selector, returnDom){
7218             var n = Roo.DomQuery.selectNode(selector, this.dom);
7219             return returnDom ? n : Roo.get(n);
7220         },
7221
7222         /**
7223          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7224          * @param {String} selector The CSS selector
7225          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7226          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7227          */
7228         down : function(selector, returnDom){
7229             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7230             return returnDom ? n : Roo.get(n);
7231         },
7232
7233         /**
7234          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7235          * @param {String} group The group the DD object is member of
7236          * @param {Object} config The DD config object
7237          * @param {Object} overrides An object containing methods to override/implement on the DD object
7238          * @return {Roo.dd.DD} The DD object
7239          */
7240         initDD : function(group, config, overrides){
7241             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7242             return Roo.apply(dd, overrides);
7243         },
7244
7245         /**
7246          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7247          * @param {String} group The group the DDProxy object is member of
7248          * @param {Object} config The DDProxy config object
7249          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7250          * @return {Roo.dd.DDProxy} The DDProxy object
7251          */
7252         initDDProxy : function(group, config, overrides){
7253             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7254             return Roo.apply(dd, overrides);
7255         },
7256
7257         /**
7258          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7259          * @param {String} group The group the DDTarget object is member of
7260          * @param {Object} config The DDTarget config object
7261          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7262          * @return {Roo.dd.DDTarget} The DDTarget object
7263          */
7264         initDDTarget : function(group, config, overrides){
7265             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7266             return Roo.apply(dd, overrides);
7267         },
7268
7269         /**
7270          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7271          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7272          * @param {Boolean} visible Whether the element is visible
7273          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7274          * @return {Roo.Element} this
7275          */
7276          setVisible : function(visible, animate){
7277             if(!animate || !A){
7278                 if(this.visibilityMode == El.DISPLAY){
7279                     this.setDisplayed(visible);
7280                 }else{
7281                     this.fixDisplay();
7282                     this.dom.style.visibility = visible ? "visible" : "hidden";
7283                 }
7284             }else{
7285                 // closure for composites
7286                 var dom = this.dom;
7287                 var visMode = this.visibilityMode;
7288                 if(visible){
7289                     this.setOpacity(.01);
7290                     this.setVisible(true);
7291                 }
7292                 this.anim({opacity: { to: (visible?1:0) }},
7293                       this.preanim(arguments, 1),
7294                       null, .35, 'easeIn', function(){
7295                          if(!visible){
7296                              if(visMode == El.DISPLAY){
7297                                  dom.style.display = "none";
7298                              }else{
7299                                  dom.style.visibility = "hidden";
7300                              }
7301                              Roo.get(dom).setOpacity(1);
7302                          }
7303                      });
7304             }
7305             return this;
7306         },
7307
7308         /**
7309          * Returns true if display is not "none"
7310          * @return {Boolean}
7311          */
7312         isDisplayed : function() {
7313             return this.getStyle("display") != "none";
7314         },
7315
7316         /**
7317          * Toggles the element's visibility or display, depending on visibility mode.
7318          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7319          * @return {Roo.Element} this
7320          */
7321         toggle : function(animate){
7322             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7323             return this;
7324         },
7325
7326         /**
7327          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7328          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7329          * @return {Roo.Element} this
7330          */
7331         setDisplayed : function(value) {
7332             if(typeof value == "boolean"){
7333                value = value ? this.originalDisplay : "none";
7334             }
7335             this.setStyle("display", value);
7336             return this;
7337         },
7338
7339         /**
7340          * Tries to focus the element. Any exceptions are caught and ignored.
7341          * @return {Roo.Element} this
7342          */
7343         focus : function() {
7344             try{
7345                 this.dom.focus();
7346             }catch(e){}
7347             return this;
7348         },
7349
7350         /**
7351          * Tries to blur the element. Any exceptions are caught and ignored.
7352          * @return {Roo.Element} this
7353          */
7354         blur : function() {
7355             try{
7356                 this.dom.blur();
7357             }catch(e){}
7358             return this;
7359         },
7360
7361         /**
7362          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7363          * @param {String/Array} className The CSS class to add, or an array of classes
7364          * @return {Roo.Element} this
7365          */
7366         addClass : function(className){
7367             if(className instanceof Array){
7368                 for(var i = 0, len = className.length; i < len; i++) {
7369                     this.addClass(className[i]);
7370                 }
7371             }else{
7372                 if(className && !this.hasClass(className)){
7373                     this.dom.className = this.dom.className + " " + className;
7374                 }
7375             }
7376             return this;
7377         },
7378
7379         /**
7380          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7381          * @param {String/Array} className The CSS class to add, or an array of classes
7382          * @return {Roo.Element} this
7383          */
7384         radioClass : function(className){
7385             var siblings = this.dom.parentNode.childNodes;
7386             for(var i = 0; i < siblings.length; i++) {
7387                 var s = siblings[i];
7388                 if(s.nodeType == 1){
7389                     Roo.get(s).removeClass(className);
7390                 }
7391             }
7392             this.addClass(className);
7393             return this;
7394         },
7395
7396         /**
7397          * Removes one or more CSS classes from the element.
7398          * @param {String/Array} className The CSS class to remove, or an array of classes
7399          * @return {Roo.Element} this
7400          */
7401         removeClass : function(className){
7402             if(!className || !this.dom.className){
7403                 return this;
7404             }
7405             if(className instanceof Array){
7406                 for(var i = 0, len = className.length; i < len; i++) {
7407                     this.removeClass(className[i]);
7408                 }
7409             }else{
7410                 if(this.hasClass(className)){
7411                     var re = this.classReCache[className];
7412                     if (!re) {
7413                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7414                        this.classReCache[className] = re;
7415                     }
7416                     this.dom.className =
7417                         this.dom.className.replace(re, " ");
7418                 }
7419             }
7420             return this;
7421         },
7422
7423         // private
7424         classReCache: {},
7425
7426         /**
7427          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7428          * @param {String} className The CSS class to toggle
7429          * @return {Roo.Element} this
7430          */
7431         toggleClass : function(className){
7432             if(this.hasClass(className)){
7433                 this.removeClass(className);
7434             }else{
7435                 this.addClass(className);
7436             }
7437             return this;
7438         },
7439
7440         /**
7441          * Checks if the specified CSS class exists on this element's DOM node.
7442          * @param {String} className The CSS class to check for
7443          * @return {Boolean} True if the class exists, else false
7444          */
7445         hasClass : function(className){
7446             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7447         },
7448
7449         /**
7450          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7451          * @param {String} oldClassName The CSS class to replace
7452          * @param {String} newClassName The replacement CSS class
7453          * @return {Roo.Element} this
7454          */
7455         replaceClass : function(oldClassName, newClassName){
7456             this.removeClass(oldClassName);
7457             this.addClass(newClassName);
7458             return this;
7459         },
7460
7461         /**
7462          * Returns an object with properties matching the styles requested.
7463          * For example, el.getStyles('color', 'font-size', 'width') might return
7464          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7465          * @param {String} style1 A style name
7466          * @param {String} style2 A style name
7467          * @param {String} etc.
7468          * @return {Object} The style object
7469          */
7470         getStyles : function(){
7471             var a = arguments, len = a.length, r = {};
7472             for(var i = 0; i < len; i++){
7473                 r[a[i]] = this.getStyle(a[i]);
7474             }
7475             return r;
7476         },
7477
7478         /**
7479          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7480          * @param {String} property The style property whose value is returned.
7481          * @return {String} The current value of the style property for this element.
7482          */
7483         getStyle : function(){
7484             return view && view.getComputedStyle ?
7485                 function(prop){
7486                     var el = this.dom, v, cs, camel;
7487                     if(prop == 'float'){
7488                         prop = "cssFloat";
7489                     }
7490                     if(el.style && (v = el.style[prop])){
7491                         return v;
7492                     }
7493                     if(cs = view.getComputedStyle(el, "")){
7494                         if(!(camel = propCache[prop])){
7495                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7496                         }
7497                         return cs[camel];
7498                     }
7499                     return null;
7500                 } :
7501                 function(prop){
7502                     var el = this.dom, v, cs, camel;
7503                     if(prop == 'opacity'){
7504                         if(typeof el.style.filter == 'string'){
7505                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7506                             if(m){
7507                                 var fv = parseFloat(m[1]);
7508                                 if(!isNaN(fv)){
7509                                     return fv ? fv / 100 : 0;
7510                                 }
7511                             }
7512                         }
7513                         return 1;
7514                     }else if(prop == 'float'){
7515                         prop = "styleFloat";
7516                     }
7517                     if(!(camel = propCache[prop])){
7518                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7519                     }
7520                     if(v = el.style[camel]){
7521                         return v;
7522                     }
7523                     if(cs = el.currentStyle){
7524                         return cs[camel];
7525                     }
7526                     return null;
7527                 };
7528         }(),
7529
7530         /**
7531          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7532          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7533          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7534          * @return {Roo.Element} this
7535          */
7536         setStyle : function(prop, value){
7537             if(typeof prop == "string"){
7538                 
7539                 if (prop == 'float') {
7540                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7541                     return this;
7542                 }
7543                 
7544                 var camel;
7545                 if(!(camel = propCache[prop])){
7546                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7547                 }
7548                 
7549                 if(camel == 'opacity') {
7550                     this.setOpacity(value);
7551                 }else{
7552                     this.dom.style[camel] = value;
7553                 }
7554             }else{
7555                 for(var style in prop){
7556                     if(typeof prop[style] != "function"){
7557                        this.setStyle(style, prop[style]);
7558                     }
7559                 }
7560             }
7561             return this;
7562         },
7563
7564         /**
7565          * More flexible version of {@link #setStyle} for setting style properties.
7566          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7567          * a function which returns such a specification.
7568          * @return {Roo.Element} this
7569          */
7570         applyStyles : function(style){
7571             Roo.DomHelper.applyStyles(this.dom, style);
7572             return this;
7573         },
7574
7575         /**
7576           * 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).
7577           * @return {Number} The X position of the element
7578           */
7579         getX : function(){
7580             return D.getX(this.dom);
7581         },
7582
7583         /**
7584           * 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).
7585           * @return {Number} The Y position of the element
7586           */
7587         getY : function(){
7588             return D.getY(this.dom);
7589         },
7590
7591         /**
7592           * 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).
7593           * @return {Array} The XY position of the element
7594           */
7595         getXY : function(){
7596             return D.getXY(this.dom);
7597         },
7598
7599         /**
7600          * 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).
7601          * @param {Number} The X position of the element
7602          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7603          * @return {Roo.Element} this
7604          */
7605         setX : function(x, animate){
7606             if(!animate || !A){
7607                 D.setX(this.dom, x);
7608             }else{
7609                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7610             }
7611             return this;
7612         },
7613
7614         /**
7615          * 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).
7616          * @param {Number} The Y position of the element
7617          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7618          * @return {Roo.Element} this
7619          */
7620         setY : function(y, animate){
7621             if(!animate || !A){
7622                 D.setY(this.dom, y);
7623             }else{
7624                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7625             }
7626             return this;
7627         },
7628
7629         /**
7630          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7631          * @param {String} left The left CSS property value
7632          * @return {Roo.Element} this
7633          */
7634         setLeft : function(left){
7635             this.setStyle("left", this.addUnits(left));
7636             return this;
7637         },
7638
7639         /**
7640          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7641          * @param {String} top The top CSS property value
7642          * @return {Roo.Element} this
7643          */
7644         setTop : function(top){
7645             this.setStyle("top", this.addUnits(top));
7646             return this;
7647         },
7648
7649         /**
7650          * Sets the element's CSS right style.
7651          * @param {String} right The right CSS property value
7652          * @return {Roo.Element} this
7653          */
7654         setRight : function(right){
7655             this.setStyle("right", this.addUnits(right));
7656             return this;
7657         },
7658
7659         /**
7660          * Sets the element's CSS bottom style.
7661          * @param {String} bottom The bottom CSS property value
7662          * @return {Roo.Element} this
7663          */
7664         setBottom : function(bottom){
7665             this.setStyle("bottom", this.addUnits(bottom));
7666             return this;
7667         },
7668
7669         /**
7670          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7671          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7672          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7673          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7674          * @return {Roo.Element} this
7675          */
7676         setXY : function(pos, animate){
7677             if(!animate || !A){
7678                 D.setXY(this.dom, pos);
7679             }else{
7680                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7681             }
7682             return this;
7683         },
7684
7685         /**
7686          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7687          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7688          * @param {Number} x X value for new position (coordinates are page-based)
7689          * @param {Number} y Y value for new position (coordinates are page-based)
7690          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7691          * @return {Roo.Element} this
7692          */
7693         setLocation : function(x, y, animate){
7694             this.setXY([x, y], this.preanim(arguments, 2));
7695             return this;
7696         },
7697
7698         /**
7699          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7700          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7701          * @param {Number} x X value for new position (coordinates are page-based)
7702          * @param {Number} y Y value for new position (coordinates are page-based)
7703          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7704          * @return {Roo.Element} this
7705          */
7706         moveTo : function(x, y, animate){
7707             this.setXY([x, y], this.preanim(arguments, 2));
7708             return this;
7709         },
7710
7711         /**
7712          * Returns the region of the given element.
7713          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7714          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7715          */
7716         getRegion : function(){
7717             return D.getRegion(this.dom);
7718         },
7719
7720         /**
7721          * Returns the offset height of the element
7722          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7723          * @return {Number} The element's height
7724          */
7725         getHeight : function(contentHeight){
7726             var h = this.dom.offsetHeight || 0;
7727             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7728         },
7729
7730         /**
7731          * Returns the offset width of the element
7732          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7733          * @return {Number} The element's width
7734          */
7735         getWidth : function(contentWidth){
7736             var w = this.dom.offsetWidth || 0;
7737             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7738         },
7739
7740         /**
7741          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7742          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7743          * if a height has not been set using CSS.
7744          * @return {Number}
7745          */
7746         getComputedHeight : function(){
7747             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7748             if(!h){
7749                 h = parseInt(this.getStyle('height'), 10) || 0;
7750                 if(!this.isBorderBox()){
7751                     h += this.getFrameWidth('tb');
7752                 }
7753             }
7754             return h;
7755         },
7756
7757         /**
7758          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7759          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7760          * if a width has not been set using CSS.
7761          * @return {Number}
7762          */
7763         getComputedWidth : function(){
7764             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7765             if(!w){
7766                 w = parseInt(this.getStyle('width'), 10) || 0;
7767                 if(!this.isBorderBox()){
7768                     w += this.getFrameWidth('lr');
7769                 }
7770             }
7771             return w;
7772         },
7773
7774         /**
7775          * Returns the size of the element.
7776          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7777          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7778          */
7779         getSize : function(contentSize){
7780             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7781         },
7782
7783         /**
7784          * Returns the width and height of the viewport.
7785          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7786          */
7787         getViewSize : function(){
7788             var d = this.dom, doc = document, aw = 0, ah = 0;
7789             if(d == doc || d == doc.body){
7790                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7791             }else{
7792                 return {
7793                     width : d.clientWidth,
7794                     height: d.clientHeight
7795                 };
7796             }
7797         },
7798
7799         /**
7800          * Returns the value of the "value" attribute
7801          * @param {Boolean} asNumber true to parse the value as a number
7802          * @return {String/Number}
7803          */
7804         getValue : function(asNumber){
7805             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7806         },
7807
7808         // private
7809         adjustWidth : function(width){
7810             if(typeof width == "number"){
7811                 if(this.autoBoxAdjust && !this.isBorderBox()){
7812                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7813                 }
7814                 if(width < 0){
7815                     width = 0;
7816                 }
7817             }
7818             return width;
7819         },
7820
7821         // private
7822         adjustHeight : function(height){
7823             if(typeof height == "number"){
7824                if(this.autoBoxAdjust && !this.isBorderBox()){
7825                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7826                }
7827                if(height < 0){
7828                    height = 0;
7829                }
7830             }
7831             return height;
7832         },
7833
7834         /**
7835          * Set the width of the element
7836          * @param {Number} width The new width
7837          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7838          * @return {Roo.Element} this
7839          */
7840         setWidth : function(width, animate){
7841             width = this.adjustWidth(width);
7842             if(!animate || !A){
7843                 this.dom.style.width = this.addUnits(width);
7844             }else{
7845                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7846             }
7847             return this;
7848         },
7849
7850         /**
7851          * Set the height of the element
7852          * @param {Number} height The new height
7853          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7854          * @return {Roo.Element} this
7855          */
7856          setHeight : function(height, animate){
7857             height = this.adjustHeight(height);
7858             if(!animate || !A){
7859                 this.dom.style.height = this.addUnits(height);
7860             }else{
7861                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7862             }
7863             return this;
7864         },
7865
7866         /**
7867          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7868          * @param {Number} width The new width
7869          * @param {Number} height The new height
7870          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7871          * @return {Roo.Element} this
7872          */
7873          setSize : function(width, height, animate){
7874             if(typeof width == "object"){ // in case of object from getSize()
7875                 height = width.height; width = width.width;
7876             }
7877             width = this.adjustWidth(width); height = this.adjustHeight(height);
7878             if(!animate || !A){
7879                 this.dom.style.width = this.addUnits(width);
7880                 this.dom.style.height = this.addUnits(height);
7881             }else{
7882                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7883             }
7884             return this;
7885         },
7886
7887         /**
7888          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7889          * @param {Number} x X value for new position (coordinates are page-based)
7890          * @param {Number} y Y value for new position (coordinates are page-based)
7891          * @param {Number} width The new width
7892          * @param {Number} height The new height
7893          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7894          * @return {Roo.Element} this
7895          */
7896         setBounds : function(x, y, width, height, animate){
7897             if(!animate || !A){
7898                 this.setSize(width, height);
7899                 this.setLocation(x, y);
7900             }else{
7901                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7902                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7903                               this.preanim(arguments, 4), 'motion');
7904             }
7905             return this;
7906         },
7907
7908         /**
7909          * 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.
7910          * @param {Roo.lib.Region} region The region to fill
7911          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7912          * @return {Roo.Element} this
7913          */
7914         setRegion : function(region, animate){
7915             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7916             return this;
7917         },
7918
7919         /**
7920          * Appends an event handler
7921          *
7922          * @param {String}   eventName     The type of event to append
7923          * @param {Function} fn        The method the event invokes
7924          * @param {Object} scope       (optional) The scope (this object) of the fn
7925          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7926          */
7927         addListener : function(eventName, fn, scope, options){
7928             if (this.dom) {
7929                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7930             }
7931         },
7932
7933         /**
7934          * Removes an event handler from this element
7935          * @param {String} eventName the type of event to remove
7936          * @param {Function} fn the method the event invokes
7937          * @return {Roo.Element} this
7938          */
7939         removeListener : function(eventName, fn){
7940             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7941             return this;
7942         },
7943
7944         /**
7945          * Removes all previous added listeners from this element
7946          * @return {Roo.Element} this
7947          */
7948         removeAllListeners : function(){
7949             E.purgeElement(this.dom);
7950             return this;
7951         },
7952
7953         relayEvent : function(eventName, observable){
7954             this.on(eventName, function(e){
7955                 observable.fireEvent(eventName, e);
7956             });
7957         },
7958
7959         /**
7960          * Set the opacity of the element
7961          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7962          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7963          * @return {Roo.Element} this
7964          */
7965          setOpacity : function(opacity, animate){
7966             if(!animate || !A){
7967                 var s = this.dom.style;
7968                 if(Roo.isIE){
7969                     s.zoom = 1;
7970                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7971                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7972                 }else{
7973                     s.opacity = opacity;
7974                 }
7975             }else{
7976                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7977             }
7978             return this;
7979         },
7980
7981         /**
7982          * Gets the left X coordinate
7983          * @param {Boolean} local True to get the local css position instead of page coordinate
7984          * @return {Number}
7985          */
7986         getLeft : function(local){
7987             if(!local){
7988                 return this.getX();
7989             }else{
7990                 return parseInt(this.getStyle("left"), 10) || 0;
7991             }
7992         },
7993
7994         /**
7995          * Gets the right X coordinate of the element (element X position + element width)
7996          * @param {Boolean} local True to get the local css position instead of page coordinate
7997          * @return {Number}
7998          */
7999         getRight : function(local){
8000             if(!local){
8001                 return this.getX() + this.getWidth();
8002             }else{
8003                 return (this.getLeft(true) + this.getWidth()) || 0;
8004             }
8005         },
8006
8007         /**
8008          * Gets the top Y coordinate
8009          * @param {Boolean} local True to get the local css position instead of page coordinate
8010          * @return {Number}
8011          */
8012         getTop : function(local) {
8013             if(!local){
8014                 return this.getY();
8015             }else{
8016                 return parseInt(this.getStyle("top"), 10) || 0;
8017             }
8018         },
8019
8020         /**
8021          * Gets the bottom Y coordinate of the element (element Y position + element height)
8022          * @param {Boolean} local True to get the local css position instead of page coordinate
8023          * @return {Number}
8024          */
8025         getBottom : function(local){
8026             if(!local){
8027                 return this.getY() + this.getHeight();
8028             }else{
8029                 return (this.getTop(true) + this.getHeight()) || 0;
8030             }
8031         },
8032
8033         /**
8034         * Initializes positioning on this element. If a desired position is not passed, it will make the
8035         * the element positioned relative IF it is not already positioned.
8036         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8037         * @param {Number} zIndex (optional) The zIndex to apply
8038         * @param {Number} x (optional) Set the page X position
8039         * @param {Number} y (optional) Set the page Y position
8040         */
8041         position : function(pos, zIndex, x, y){
8042             if(!pos){
8043                if(this.getStyle('position') == 'static'){
8044                    this.setStyle('position', 'relative');
8045                }
8046             }else{
8047                 this.setStyle("position", pos);
8048             }
8049             if(zIndex){
8050                 this.setStyle("z-index", zIndex);
8051             }
8052             if(x !== undefined && y !== undefined){
8053                 this.setXY([x, y]);
8054             }else if(x !== undefined){
8055                 this.setX(x);
8056             }else if(y !== undefined){
8057                 this.setY(y);
8058             }
8059         },
8060
8061         /**
8062         * Clear positioning back to the default when the document was loaded
8063         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8064         * @return {Roo.Element} this
8065          */
8066         clearPositioning : function(value){
8067             value = value ||'';
8068             this.setStyle({
8069                 "left": value,
8070                 "right": value,
8071                 "top": value,
8072                 "bottom": value,
8073                 "z-index": "",
8074                 "position" : "static"
8075             });
8076             return this;
8077         },
8078
8079         /**
8080         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8081         * snapshot before performing an update and then restoring the element.
8082         * @return {Object}
8083         */
8084         getPositioning : function(){
8085             var l = this.getStyle("left");
8086             var t = this.getStyle("top");
8087             return {
8088                 "position" : this.getStyle("position"),
8089                 "left" : l,
8090                 "right" : l ? "" : this.getStyle("right"),
8091                 "top" : t,
8092                 "bottom" : t ? "" : this.getStyle("bottom"),
8093                 "z-index" : this.getStyle("z-index")
8094             };
8095         },
8096
8097         /**
8098          * Gets the width of the border(s) for the specified side(s)
8099          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8100          * passing lr would get the border (l)eft width + the border (r)ight width.
8101          * @return {Number} The width of the sides passed added together
8102          */
8103         getBorderWidth : function(side){
8104             return this.addStyles(side, El.borders);
8105         },
8106
8107         /**
8108          * Gets the width of the padding(s) for the specified side(s)
8109          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8110          * passing lr would get the padding (l)eft + the padding (r)ight.
8111          * @return {Number} The padding of the sides passed added together
8112          */
8113         getPadding : function(side){
8114             return this.addStyles(side, El.paddings);
8115         },
8116
8117         /**
8118         * Set positioning with an object returned by getPositioning().
8119         * @param {Object} posCfg
8120         * @return {Roo.Element} this
8121          */
8122         setPositioning : function(pc){
8123             this.applyStyles(pc);
8124             if(pc.right == "auto"){
8125                 this.dom.style.right = "";
8126             }
8127             if(pc.bottom == "auto"){
8128                 this.dom.style.bottom = "";
8129             }
8130             return this;
8131         },
8132
8133         // private
8134         fixDisplay : function(){
8135             if(this.getStyle("display") == "none"){
8136                 this.setStyle("visibility", "hidden");
8137                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8138                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8139                     this.setStyle("display", "block");
8140                 }
8141             }
8142         },
8143
8144         /**
8145          * Quick set left and top adding default units
8146          * @param {String} left The left CSS property value
8147          * @param {String} top The top CSS property value
8148          * @return {Roo.Element} this
8149          */
8150          setLeftTop : function(left, top){
8151             this.dom.style.left = this.addUnits(left);
8152             this.dom.style.top = this.addUnits(top);
8153             return this;
8154         },
8155
8156         /**
8157          * Move this element relative to its current position.
8158          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8159          * @param {Number} distance How far to move the element in pixels
8160          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8161          * @return {Roo.Element} this
8162          */
8163          move : function(direction, distance, animate){
8164             var xy = this.getXY();
8165             direction = direction.toLowerCase();
8166             switch(direction){
8167                 case "l":
8168                 case "left":
8169                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8170                     break;
8171                case "r":
8172                case "right":
8173                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8174                     break;
8175                case "t":
8176                case "top":
8177                case "up":
8178                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8179                     break;
8180                case "b":
8181                case "bottom":
8182                case "down":
8183                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8184                     break;
8185             }
8186             return this;
8187         },
8188
8189         /**
8190          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8191          * @return {Roo.Element} this
8192          */
8193         clip : function(){
8194             if(!this.isClipped){
8195                this.isClipped = true;
8196                this.originalClip = {
8197                    "o": this.getStyle("overflow"),
8198                    "x": this.getStyle("overflow-x"),
8199                    "y": this.getStyle("overflow-y")
8200                };
8201                this.setStyle("overflow", "hidden");
8202                this.setStyle("overflow-x", "hidden");
8203                this.setStyle("overflow-y", "hidden");
8204             }
8205             return this;
8206         },
8207
8208         /**
8209          *  Return clipping (overflow) to original clipping before clip() was called
8210          * @return {Roo.Element} this
8211          */
8212         unclip : function(){
8213             if(this.isClipped){
8214                 this.isClipped = false;
8215                 var o = this.originalClip;
8216                 if(o.o){this.setStyle("overflow", o.o);}
8217                 if(o.x){this.setStyle("overflow-x", o.x);}
8218                 if(o.y){this.setStyle("overflow-y", o.y);}
8219             }
8220             return this;
8221         },
8222
8223
8224         /**
8225          * Gets the x,y coordinates specified by the anchor position on the element.
8226          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8227          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8228          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8229          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8230          * @return {Array} [x, y] An array containing the element's x and y coordinates
8231          */
8232         getAnchorXY : function(anchor, local, s){
8233             //Passing a different size is useful for pre-calculating anchors,
8234             //especially for anchored animations that change the el size.
8235
8236             var w, h, vp = false;
8237             if(!s){
8238                 var d = this.dom;
8239                 if(d == document.body || d == document){
8240                     vp = true;
8241                     w = D.getViewWidth(); h = D.getViewHeight();
8242                 }else{
8243                     w = this.getWidth(); h = this.getHeight();
8244                 }
8245             }else{
8246                 w = s.width;  h = s.height;
8247             }
8248             var x = 0, y = 0, r = Math.round;
8249             switch((anchor || "tl").toLowerCase()){
8250                 case "c":
8251                     x = r(w*.5);
8252                     y = r(h*.5);
8253                 break;
8254                 case "t":
8255                     x = r(w*.5);
8256                     y = 0;
8257                 break;
8258                 case "l":
8259                     x = 0;
8260                     y = r(h*.5);
8261                 break;
8262                 case "r":
8263                     x = w;
8264                     y = r(h*.5);
8265                 break;
8266                 case "b":
8267                     x = r(w*.5);
8268                     y = h;
8269                 break;
8270                 case "tl":
8271                     x = 0;
8272                     y = 0;
8273                 break;
8274                 case "bl":
8275                     x = 0;
8276                     y = h;
8277                 break;
8278                 case "br":
8279                     x = w;
8280                     y = h;
8281                 break;
8282                 case "tr":
8283                     x = w;
8284                     y = 0;
8285                 break;
8286             }
8287             if(local === true){
8288                 return [x, y];
8289             }
8290             if(vp){
8291                 var sc = this.getScroll();
8292                 return [x + sc.left, y + sc.top];
8293             }
8294             //Add the element's offset xy
8295             var o = this.getXY();
8296             return [x+o[0], y+o[1]];
8297         },
8298
8299         /**
8300          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8301          * supported position values.
8302          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8303          * @param {String} position The position to align to.
8304          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8305          * @return {Array} [x, y]
8306          */
8307         getAlignToXY : function(el, p, o){
8308             el = Roo.get(el);
8309             var d = this.dom;
8310             if(!el.dom){
8311                 throw "Element.alignTo with an element that doesn't exist";
8312             }
8313             var c = false; //constrain to viewport
8314             var p1 = "", p2 = "";
8315             o = o || [0,0];
8316
8317             if(!p){
8318                 p = "tl-bl";
8319             }else if(p == "?"){
8320                 p = "tl-bl?";
8321             }else if(p.indexOf("-") == -1){
8322                 p = "tl-" + p;
8323             }
8324             p = p.toLowerCase();
8325             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8326             if(!m){
8327                throw "Element.alignTo with an invalid alignment " + p;
8328             }
8329             p1 = m[1]; p2 = m[2]; c = !!m[3];
8330
8331             //Subtract the aligned el's internal xy from the target's offset xy
8332             //plus custom offset to get the aligned el's new offset xy
8333             var a1 = this.getAnchorXY(p1, true);
8334             var a2 = el.getAnchorXY(p2, false);
8335             var x = a2[0] - a1[0] + o[0];
8336             var y = a2[1] - a1[1] + o[1];
8337             if(c){
8338                 //constrain the aligned el to viewport if necessary
8339                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8340                 // 5px of margin for ie
8341                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8342
8343                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8344                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8345                 //otherwise swap the aligned el to the opposite border of the target.
8346                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8347                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8348                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8349                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8350
8351                var doc = document;
8352                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8353                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8354
8355                if((x+w) > dw + scrollX){
8356                     x = swapX ? r.left-w : dw+scrollX-w;
8357                 }
8358                if(x < scrollX){
8359                    x = swapX ? r.right : scrollX;
8360                }
8361                if((y+h) > dh + scrollY){
8362                     y = swapY ? r.top-h : dh+scrollY-h;
8363                 }
8364                if (y < scrollY){
8365                    y = swapY ? r.bottom : scrollY;
8366                }
8367             }
8368             return [x,y];
8369         },
8370
8371         // private
8372         getConstrainToXY : function(){
8373             var os = {top:0, left:0, bottom:0, right: 0};
8374
8375             return function(el, local, offsets, proposedXY){
8376                 el = Roo.get(el);
8377                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8378
8379                 var vw, vh, vx = 0, vy = 0;
8380                 if(el.dom == document.body || el.dom == document){
8381                     vw = Roo.lib.Dom.getViewWidth();
8382                     vh = Roo.lib.Dom.getViewHeight();
8383                 }else{
8384                     vw = el.dom.clientWidth;
8385                     vh = el.dom.clientHeight;
8386                     if(!local){
8387                         var vxy = el.getXY();
8388                         vx = vxy[0];
8389                         vy = vxy[1];
8390                     }
8391                 }
8392
8393                 var s = el.getScroll();
8394
8395                 vx += offsets.left + s.left;
8396                 vy += offsets.top + s.top;
8397
8398                 vw -= offsets.right;
8399                 vh -= offsets.bottom;
8400
8401                 var vr = vx+vw;
8402                 var vb = vy+vh;
8403
8404                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8405                 var x = xy[0], y = xy[1];
8406                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8407
8408                 // only move it if it needs it
8409                 var moved = false;
8410
8411                 // first validate right/bottom
8412                 if((x + w) > vr){
8413                     x = vr - w;
8414                     moved = true;
8415                 }
8416                 if((y + h) > vb){
8417                     y = vb - h;
8418                     moved = true;
8419                 }
8420                 // then make sure top/left isn't negative
8421                 if(x < vx){
8422                     x = vx;
8423                     moved = true;
8424                 }
8425                 if(y < vy){
8426                     y = vy;
8427                     moved = true;
8428                 }
8429                 return moved ? [x, y] : false;
8430             };
8431         }(),
8432
8433         // private
8434         adjustForConstraints : function(xy, parent, offsets){
8435             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8436         },
8437
8438         /**
8439          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8440          * document it aligns it to the viewport.
8441          * The position parameter is optional, and can be specified in any one of the following formats:
8442          * <ul>
8443          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8444          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8445          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8446          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8447          *   <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
8448          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8449          * </ul>
8450          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8451          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8452          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8453          * that specified in order to enforce the viewport constraints.
8454          * Following are all of the supported anchor positions:
8455     <pre>
8456     Value  Description
8457     -----  -----------------------------
8458     tl     The top left corner (default)
8459     t      The center of the top edge
8460     tr     The top right corner
8461     l      The center of the left edge
8462     c      In the center of the element
8463     r      The center of the right edge
8464     bl     The bottom left corner
8465     b      The center of the bottom edge
8466     br     The bottom right corner
8467     </pre>
8468     Example Usage:
8469     <pre><code>
8470     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8471     el.alignTo("other-el");
8472
8473     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8474     el.alignTo("other-el", "tr?");
8475
8476     // align the bottom right corner of el with the center left edge of other-el
8477     el.alignTo("other-el", "br-l?");
8478
8479     // align the center of el with the bottom left corner of other-el and
8480     // adjust the x position by -6 pixels (and the y position by 0)
8481     el.alignTo("other-el", "c-bl", [-6, 0]);
8482     </code></pre>
8483          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8484          * @param {String} position The position to align to.
8485          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8486          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8487          * @return {Roo.Element} this
8488          */
8489         alignTo : function(element, position, offsets, animate){
8490             var xy = this.getAlignToXY(element, position, offsets);
8491             this.setXY(xy, this.preanim(arguments, 3));
8492             return this;
8493         },
8494
8495         /**
8496          * Anchors an element to another element and realigns it when the window is resized.
8497          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8498          * @param {String} position The position to align to.
8499          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8500          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8501          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8502          * is a number, it is used as the buffer delay (defaults to 50ms).
8503          * @param {Function} callback The function to call after the animation finishes
8504          * @return {Roo.Element} this
8505          */
8506         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8507             var action = function(){
8508                 this.alignTo(el, alignment, offsets, animate);
8509                 Roo.callback(callback, this);
8510             };
8511             Roo.EventManager.onWindowResize(action, this);
8512             var tm = typeof monitorScroll;
8513             if(tm != 'undefined'){
8514                 Roo.EventManager.on(window, 'scroll', action, this,
8515                     {buffer: tm == 'number' ? monitorScroll : 50});
8516             }
8517             action.call(this); // align immediately
8518             return this;
8519         },
8520         /**
8521          * Clears any opacity settings from this element. Required in some cases for IE.
8522          * @return {Roo.Element} this
8523          */
8524         clearOpacity : function(){
8525             if (window.ActiveXObject) {
8526                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8527                     this.dom.style.filter = "";
8528                 }
8529             } else {
8530                 this.dom.style.opacity = "";
8531                 this.dom.style["-moz-opacity"] = "";
8532                 this.dom.style["-khtml-opacity"] = "";
8533             }
8534             return this;
8535         },
8536
8537         /**
8538          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8539          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8540          * @return {Roo.Element} this
8541          */
8542         hide : function(animate){
8543             this.setVisible(false, this.preanim(arguments, 0));
8544             return this;
8545         },
8546
8547         /**
8548         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8549         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8550          * @return {Roo.Element} this
8551          */
8552         show : function(animate){
8553             this.setVisible(true, this.preanim(arguments, 0));
8554             return this;
8555         },
8556
8557         /**
8558          * @private Test if size has a unit, otherwise appends the default
8559          */
8560         addUnits : function(size){
8561             return Roo.Element.addUnits(size, this.defaultUnit);
8562         },
8563
8564         /**
8565          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8566          * @return {Roo.Element} this
8567          */
8568         beginMeasure : function(){
8569             var el = this.dom;
8570             if(el.offsetWidth || el.offsetHeight){
8571                 return this; // offsets work already
8572             }
8573             var changed = [];
8574             var p = this.dom, b = document.body; // start with this element
8575             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8576                 var pe = Roo.get(p);
8577                 if(pe.getStyle('display') == 'none'){
8578                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8579                     p.style.visibility = "hidden";
8580                     p.style.display = "block";
8581                 }
8582                 p = p.parentNode;
8583             }
8584             this._measureChanged = changed;
8585             return this;
8586
8587         },
8588
8589         /**
8590          * Restores displays to before beginMeasure was called
8591          * @return {Roo.Element} this
8592          */
8593         endMeasure : function(){
8594             var changed = this._measureChanged;
8595             if(changed){
8596                 for(var i = 0, len = changed.length; i < len; i++) {
8597                     var r = changed[i];
8598                     r.el.style.visibility = r.visibility;
8599                     r.el.style.display = "none";
8600                 }
8601                 this._measureChanged = null;
8602             }
8603             return this;
8604         },
8605
8606         /**
8607         * Update the innerHTML of this element, optionally searching for and processing scripts
8608         * @param {String} html The new HTML
8609         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8610         * @param {Function} callback For async script loading you can be noticed when the update completes
8611         * @return {Roo.Element} this
8612          */
8613         update : function(html, loadScripts, callback){
8614             if(typeof html == "undefined"){
8615                 html = "";
8616             }
8617             if(loadScripts !== true){
8618                 this.dom.innerHTML = html;
8619                 if(typeof callback == "function"){
8620                     callback();
8621                 }
8622                 return this;
8623             }
8624             var id = Roo.id();
8625             var dom = this.dom;
8626
8627             html += '<span id="' + id + '"></span>';
8628
8629             E.onAvailable(id, function(){
8630                 var hd = document.getElementsByTagName("head")[0];
8631                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8632                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8633                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8634
8635                 var match;
8636                 while(match = re.exec(html)){
8637                     var attrs = match[1];
8638                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8639                     if(srcMatch && srcMatch[2]){
8640                        var s = document.createElement("script");
8641                        s.src = srcMatch[2];
8642                        var typeMatch = attrs.match(typeRe);
8643                        if(typeMatch && typeMatch[2]){
8644                            s.type = typeMatch[2];
8645                        }
8646                        hd.appendChild(s);
8647                     }else if(match[2] && match[2].length > 0){
8648                         if(window.execScript) {
8649                            window.execScript(match[2]);
8650                         } else {
8651                             /**
8652                              * eval:var:id
8653                              * eval:var:dom
8654                              * eval:var:html
8655                              * 
8656                              */
8657                            window.eval(match[2]);
8658                         }
8659                     }
8660                 }
8661                 var el = document.getElementById(id);
8662                 if(el){el.parentNode.removeChild(el);}
8663                 if(typeof callback == "function"){
8664                     callback();
8665                 }
8666             });
8667             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8668             return this;
8669         },
8670
8671         /**
8672          * Direct access to the UpdateManager update() method (takes the same parameters).
8673          * @param {String/Function} url The url for this request or a function to call to get the url
8674          * @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}
8675          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8676          * @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.
8677          * @return {Roo.Element} this
8678          */
8679         load : function(){
8680             var um = this.getUpdateManager();
8681             um.update.apply(um, arguments);
8682             return this;
8683         },
8684
8685         /**
8686         * Gets this element's UpdateManager
8687         * @return {Roo.UpdateManager} The UpdateManager
8688         */
8689         getUpdateManager : function(){
8690             if(!this.updateManager){
8691                 this.updateManager = new Roo.UpdateManager(this);
8692             }
8693             return this.updateManager;
8694         },
8695
8696         /**
8697          * Disables text selection for this element (normalized across browsers)
8698          * @return {Roo.Element} this
8699          */
8700         unselectable : function(){
8701             this.dom.unselectable = "on";
8702             this.swallowEvent("selectstart", true);
8703             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8704             this.addClass("x-unselectable");
8705             return this;
8706         },
8707
8708         /**
8709         * Calculates the x, y to center this element on the screen
8710         * @return {Array} The x, y values [x, y]
8711         */
8712         getCenterXY : function(){
8713             return this.getAlignToXY(document, 'c-c');
8714         },
8715
8716         /**
8717         * Centers the Element in either the viewport, or another Element.
8718         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8719         */
8720         center : function(centerIn){
8721             this.alignTo(centerIn || document, 'c-c');
8722             return this;
8723         },
8724
8725         /**
8726          * Tests various css rules/browsers to determine if this element uses a border box
8727          * @return {Boolean}
8728          */
8729         isBorderBox : function(){
8730             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8731         },
8732
8733         /**
8734          * Return a box {x, y, width, height} that can be used to set another elements
8735          * size/location to match this element.
8736          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8737          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8738          * @return {Object} box An object in the format {x, y, width, height}
8739          */
8740         getBox : function(contentBox, local){
8741             var xy;
8742             if(!local){
8743                 xy = this.getXY();
8744             }else{
8745                 var left = parseInt(this.getStyle("left"), 10) || 0;
8746                 var top = parseInt(this.getStyle("top"), 10) || 0;
8747                 xy = [left, top];
8748             }
8749             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8750             if(!contentBox){
8751                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8752             }else{
8753                 var l = this.getBorderWidth("l")+this.getPadding("l");
8754                 var r = this.getBorderWidth("r")+this.getPadding("r");
8755                 var t = this.getBorderWidth("t")+this.getPadding("t");
8756                 var b = this.getBorderWidth("b")+this.getPadding("b");
8757                 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)};
8758             }
8759             bx.right = bx.x + bx.width;
8760             bx.bottom = bx.y + bx.height;
8761             return bx;
8762         },
8763
8764         /**
8765          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8766          for more information about the sides.
8767          * @param {String} sides
8768          * @return {Number}
8769          */
8770         getFrameWidth : function(sides, onlyContentBox){
8771             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8772         },
8773
8774         /**
8775          * 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.
8776          * @param {Object} box The box to fill {x, y, width, height}
8777          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8778          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8779          * @return {Roo.Element} this
8780          */
8781         setBox : function(box, adjust, animate){
8782             var w = box.width, h = box.height;
8783             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8784                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8785                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8786             }
8787             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8788             return this;
8789         },
8790
8791         /**
8792          * Forces the browser to repaint this element
8793          * @return {Roo.Element} this
8794          */
8795          repaint : function(){
8796             var dom = this.dom;
8797             this.addClass("x-repaint");
8798             setTimeout(function(){
8799                 Roo.get(dom).removeClass("x-repaint");
8800             }, 1);
8801             return this;
8802         },
8803
8804         /**
8805          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8806          * then it returns the calculated width of the sides (see getPadding)
8807          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8808          * @return {Object/Number}
8809          */
8810         getMargins : function(side){
8811             if(!side){
8812                 return {
8813                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8814                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8815                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8816                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8817                 };
8818             }else{
8819                 return this.addStyles(side, El.margins);
8820              }
8821         },
8822
8823         // private
8824         addStyles : function(sides, styles){
8825             var val = 0, v, w;
8826             for(var i = 0, len = sides.length; i < len; i++){
8827                 v = this.getStyle(styles[sides.charAt(i)]);
8828                 if(v){
8829                      w = parseInt(v, 10);
8830                      if(w){ val += w; }
8831                 }
8832             }
8833             return val;
8834         },
8835
8836         /**
8837          * Creates a proxy element of this element
8838          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8839          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8840          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8841          * @return {Roo.Element} The new proxy element
8842          */
8843         createProxy : function(config, renderTo, matchBox){
8844             if(renderTo){
8845                 renderTo = Roo.getDom(renderTo);
8846             }else{
8847                 renderTo = document.body;
8848             }
8849             config = typeof config == "object" ?
8850                 config : {tag : "div", cls: config};
8851             var proxy = Roo.DomHelper.append(renderTo, config, true);
8852             if(matchBox){
8853                proxy.setBox(this.getBox());
8854             }
8855             return proxy;
8856         },
8857
8858         /**
8859          * Puts a mask over this element to disable user interaction. Requires core.css.
8860          * This method can only be applied to elements which accept child nodes.
8861          * @param {String} msg (optional) A message to display in the mask
8862          * @param {String} msgCls (optional) A css class to apply to the msg element
8863          * @return {Element} The mask  element
8864          */
8865         mask : function(msg, msgCls)
8866         {
8867             if(this.getStyle("position") == "static"){
8868                 this.setStyle("position", "relative");
8869             }
8870             if(!this._mask){
8871                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8872             }
8873             this.addClass("x-masked");
8874             this._mask.setDisplayed(true);
8875             
8876             // we wander
8877             var z = 0;
8878             var dom = this.dom
8879             while (dom && dom.style) {
8880                 if (!isNaN(parseInt(dom.style.zIndex))) {
8881                     z = Math.max(z, parseInt(dom.style.zIndex));
8882                 }
8883                 dom = dom.parentNode;
8884             }
8885             // if we are masking the body - then it hides everything..
8886             if (this.dom == document.body) {
8887                 z = 1000000;
8888                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8889                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8890             }
8891            
8892             if(typeof msg == 'string'){
8893                 if(!this._maskMsg){
8894                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8895                 }
8896                 var mm = this._maskMsg;
8897                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8898                 mm.dom.firstChild.innerHTML = msg;
8899                 mm.setDisplayed(true);
8900                 mm.center(this);
8901                 mm.setStyle('z-index', z + 102);
8902             }
8903             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8904                 this._mask.setHeight(this.getHeight());
8905             }
8906             this._mask.setStyle('z-index', z + 100);
8907             
8908             return this._mask;
8909         },
8910
8911         /**
8912          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8913          * it is cached for reuse.
8914          */
8915         unmask : function(removeEl){
8916             if(this._mask){
8917                 if(removeEl === true){
8918                     this._mask.remove();
8919                     delete this._mask;
8920                     if(this._maskMsg){
8921                         this._maskMsg.remove();
8922                         delete this._maskMsg;
8923                     }
8924                 }else{
8925                     this._mask.setDisplayed(false);
8926                     if(this._maskMsg){
8927                         this._maskMsg.setDisplayed(false);
8928                     }
8929                 }
8930             }
8931             this.removeClass("x-masked");
8932         },
8933
8934         /**
8935          * Returns true if this element is masked
8936          * @return {Boolean}
8937          */
8938         isMasked : function(){
8939             return this._mask && this._mask.isVisible();
8940         },
8941
8942         /**
8943          * Creates an iframe shim for this element to keep selects and other windowed objects from
8944          * showing through.
8945          * @return {Roo.Element} The new shim element
8946          */
8947         createShim : function(){
8948             var el = document.createElement('iframe');
8949             el.frameBorder = 'no';
8950             el.className = 'roo-shim';
8951             if(Roo.isIE && Roo.isSecure){
8952                 el.src = Roo.SSL_SECURE_URL;
8953             }
8954             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8955             shim.autoBoxAdjust = false;
8956             return shim;
8957         },
8958
8959         /**
8960          * Removes this element from the DOM and deletes it from the cache
8961          */
8962         remove : function(){
8963             if(this.dom.parentNode){
8964                 this.dom.parentNode.removeChild(this.dom);
8965             }
8966             delete El.cache[this.dom.id];
8967         },
8968
8969         /**
8970          * Sets up event handlers to add and remove a css class when the mouse is over this element
8971          * @param {String} className
8972          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8973          * mouseout events for children elements
8974          * @return {Roo.Element} this
8975          */
8976         addClassOnOver : function(className, preventFlicker){
8977             this.on("mouseover", function(){
8978                 Roo.fly(this, '_internal').addClass(className);
8979             }, this.dom);
8980             var removeFn = function(e){
8981                 if(preventFlicker !== true || !e.within(this, true)){
8982                     Roo.fly(this, '_internal').removeClass(className);
8983                 }
8984             };
8985             this.on("mouseout", removeFn, this.dom);
8986             return this;
8987         },
8988
8989         /**
8990          * Sets up event handlers to add and remove a css class when this element has the focus
8991          * @param {String} className
8992          * @return {Roo.Element} this
8993          */
8994         addClassOnFocus : function(className){
8995             this.on("focus", function(){
8996                 Roo.fly(this, '_internal').addClass(className);
8997             }, this.dom);
8998             this.on("blur", function(){
8999                 Roo.fly(this, '_internal').removeClass(className);
9000             }, this.dom);
9001             return this;
9002         },
9003         /**
9004          * 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)
9005          * @param {String} className
9006          * @return {Roo.Element} this
9007          */
9008         addClassOnClick : function(className){
9009             var dom = this.dom;
9010             this.on("mousedown", function(){
9011                 Roo.fly(dom, '_internal').addClass(className);
9012                 var d = Roo.get(document);
9013                 var fn = function(){
9014                     Roo.fly(dom, '_internal').removeClass(className);
9015                     d.removeListener("mouseup", fn);
9016                 };
9017                 d.on("mouseup", fn);
9018             });
9019             return this;
9020         },
9021
9022         /**
9023          * Stops the specified event from bubbling and optionally prevents the default action
9024          * @param {String} eventName
9025          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9026          * @return {Roo.Element} this
9027          */
9028         swallowEvent : function(eventName, preventDefault){
9029             var fn = function(e){
9030                 e.stopPropagation();
9031                 if(preventDefault){
9032                     e.preventDefault();
9033                 }
9034             };
9035             if(eventName instanceof Array){
9036                 for(var i = 0, len = eventName.length; i < len; i++){
9037                      this.on(eventName[i], fn);
9038                 }
9039                 return this;
9040             }
9041             this.on(eventName, fn);
9042             return this;
9043         },
9044
9045         /**
9046          * @private
9047          */
9048       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9049
9050         /**
9051          * Sizes this element to its parent element's dimensions performing
9052          * neccessary box adjustments.
9053          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9054          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9055          * @return {Roo.Element} this
9056          */
9057         fitToParent : function(monitorResize, targetParent) {
9058           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9059           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9060           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9061             return;
9062           }
9063           var p = Roo.get(targetParent || this.dom.parentNode);
9064           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9065           if (monitorResize === true) {
9066             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9067             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9068           }
9069           return this;
9070         },
9071
9072         /**
9073          * Gets the next sibling, skipping text nodes
9074          * @return {HTMLElement} The next sibling or null
9075          */
9076         getNextSibling : function(){
9077             var n = this.dom.nextSibling;
9078             while(n && n.nodeType != 1){
9079                 n = n.nextSibling;
9080             }
9081             return n;
9082         },
9083
9084         /**
9085          * Gets the previous sibling, skipping text nodes
9086          * @return {HTMLElement} The previous sibling or null
9087          */
9088         getPrevSibling : function(){
9089             var n = this.dom.previousSibling;
9090             while(n && n.nodeType != 1){
9091                 n = n.previousSibling;
9092             }
9093             return n;
9094         },
9095
9096
9097         /**
9098          * Appends the passed element(s) to this element
9099          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9100          * @return {Roo.Element} this
9101          */
9102         appendChild: function(el){
9103             el = Roo.get(el);
9104             el.appendTo(this);
9105             return this;
9106         },
9107
9108         /**
9109          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9110          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9111          * automatically generated with the specified attributes.
9112          * @param {HTMLElement} insertBefore (optional) a child element of this element
9113          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9114          * @return {Roo.Element} The new child element
9115          */
9116         createChild: function(config, insertBefore, returnDom){
9117             config = config || {tag:'div'};
9118             if(insertBefore){
9119                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9120             }
9121             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9122         },
9123
9124         /**
9125          * Appends this element to the passed element
9126          * @param {String/HTMLElement/Element} el The new parent element
9127          * @return {Roo.Element} this
9128          */
9129         appendTo: function(el){
9130             el = Roo.getDom(el);
9131             el.appendChild(this.dom);
9132             return this;
9133         },
9134
9135         /**
9136          * Inserts this element before the passed element in the DOM
9137          * @param {String/HTMLElement/Element} el The element to insert before
9138          * @return {Roo.Element} this
9139          */
9140         insertBefore: function(el){
9141             el = Roo.getDom(el);
9142             el.parentNode.insertBefore(this.dom, el);
9143             return this;
9144         },
9145
9146         /**
9147          * Inserts this element after the passed element in the DOM
9148          * @param {String/HTMLElement/Element} el The element to insert after
9149          * @return {Roo.Element} this
9150          */
9151         insertAfter: function(el){
9152             el = Roo.getDom(el);
9153             el.parentNode.insertBefore(this.dom, el.nextSibling);
9154             return this;
9155         },
9156
9157         /**
9158          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9159          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9160          * @return {Roo.Element} The new child
9161          */
9162         insertFirst: function(el, returnDom){
9163             el = el || {};
9164             if(typeof el == 'object' && !el.nodeType){ // dh config
9165                 return this.createChild(el, this.dom.firstChild, returnDom);
9166             }else{
9167                 el = Roo.getDom(el);
9168                 this.dom.insertBefore(el, this.dom.firstChild);
9169                 return !returnDom ? Roo.get(el) : el;
9170             }
9171         },
9172
9173         /**
9174          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9175          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9176          * @param {String} where (optional) 'before' or 'after' defaults to before
9177          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9178          * @return {Roo.Element} the inserted Element
9179          */
9180         insertSibling: function(el, where, returnDom){
9181             where = where ? where.toLowerCase() : 'before';
9182             el = el || {};
9183             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9184
9185             if(typeof el == 'object' && !el.nodeType){ // dh config
9186                 if(where == 'after' && !this.dom.nextSibling){
9187                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9188                 }else{
9189                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9190                 }
9191
9192             }else{
9193                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9194                             where == 'before' ? this.dom : this.dom.nextSibling);
9195                 if(!returnDom){
9196                     rt = Roo.get(rt);
9197                 }
9198             }
9199             return rt;
9200         },
9201
9202         /**
9203          * Creates and wraps this element with another element
9204          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9205          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9206          * @return {HTMLElement/Element} The newly created wrapper element
9207          */
9208         wrap: function(config, returnDom){
9209             if(!config){
9210                 config = {tag: "div"};
9211             }
9212             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9213             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9214             return newEl;
9215         },
9216
9217         /**
9218          * Replaces the passed element with this element
9219          * @param {String/HTMLElement/Element} el The element to replace
9220          * @return {Roo.Element} this
9221          */
9222         replace: function(el){
9223             el = Roo.get(el);
9224             this.insertBefore(el);
9225             el.remove();
9226             return this;
9227         },
9228
9229         /**
9230          * Inserts an html fragment into this element
9231          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9232          * @param {String} html The HTML fragment
9233          * @param {Boolean} returnEl True to return an Roo.Element
9234          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9235          */
9236         insertHtml : function(where, html, returnEl){
9237             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9238             return returnEl ? Roo.get(el) : el;
9239         },
9240
9241         /**
9242          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9243          * @param {Object} o The object with the attributes
9244          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9245          * @return {Roo.Element} this
9246          */
9247         set : function(o, useSet){
9248             var el = this.dom;
9249             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9250             for(var attr in o){
9251                 if(attr == "style" || typeof o[attr] == "function") continue;
9252                 if(attr=="cls"){
9253                     el.className = o["cls"];
9254                 }else{
9255                     if(useSet) el.setAttribute(attr, o[attr]);
9256                     else el[attr] = o[attr];
9257                 }
9258             }
9259             if(o.style){
9260                 Roo.DomHelper.applyStyles(el, o.style);
9261             }
9262             return this;
9263         },
9264
9265         /**
9266          * Convenience method for constructing a KeyMap
9267          * @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:
9268          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9269          * @param {Function} fn The function to call
9270          * @param {Object} scope (optional) The scope of the function
9271          * @return {Roo.KeyMap} The KeyMap created
9272          */
9273         addKeyListener : function(key, fn, scope){
9274             var config;
9275             if(typeof key != "object" || key instanceof Array){
9276                 config = {
9277                     key: key,
9278                     fn: fn,
9279                     scope: scope
9280                 };
9281             }else{
9282                 config = {
9283                     key : key.key,
9284                     shift : key.shift,
9285                     ctrl : key.ctrl,
9286                     alt : key.alt,
9287                     fn: fn,
9288                     scope: scope
9289                 };
9290             }
9291             return new Roo.KeyMap(this, config);
9292         },
9293
9294         /**
9295          * Creates a KeyMap for this element
9296          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9297          * @return {Roo.KeyMap} The KeyMap created
9298          */
9299         addKeyMap : function(config){
9300             return new Roo.KeyMap(this, config);
9301         },
9302
9303         /**
9304          * Returns true if this element is scrollable.
9305          * @return {Boolean}
9306          */
9307          isScrollable : function(){
9308             var dom = this.dom;
9309             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9310         },
9311
9312         /**
9313          * 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().
9314          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9315          * @param {Number} value The new scroll value
9316          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9317          * @return {Element} this
9318          */
9319
9320         scrollTo : function(side, value, animate){
9321             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9322             if(!animate || !A){
9323                 this.dom[prop] = value;
9324             }else{
9325                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9326                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9327             }
9328             return this;
9329         },
9330
9331         /**
9332          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9333          * within this element's scrollable range.
9334          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9335          * @param {Number} distance How far to scroll the element in pixels
9336          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9337          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9338          * was scrolled as far as it could go.
9339          */
9340          scroll : function(direction, distance, animate){
9341              if(!this.isScrollable()){
9342                  return;
9343              }
9344              var el = this.dom;
9345              var l = el.scrollLeft, t = el.scrollTop;
9346              var w = el.scrollWidth, h = el.scrollHeight;
9347              var cw = el.clientWidth, ch = el.clientHeight;
9348              direction = direction.toLowerCase();
9349              var scrolled = false;
9350              var a = this.preanim(arguments, 2);
9351              switch(direction){
9352                  case "l":
9353                  case "left":
9354                      if(w - l > cw){
9355                          var v = Math.min(l + distance, w-cw);
9356                          this.scrollTo("left", v, a);
9357                          scrolled = true;
9358                      }
9359                      break;
9360                 case "r":
9361                 case "right":
9362                      if(l > 0){
9363                          var v = Math.max(l - distance, 0);
9364                          this.scrollTo("left", v, a);
9365                          scrolled = true;
9366                      }
9367                      break;
9368                 case "t":
9369                 case "top":
9370                 case "up":
9371                      if(t > 0){
9372                          var v = Math.max(t - distance, 0);
9373                          this.scrollTo("top", v, a);
9374                          scrolled = true;
9375                      }
9376                      break;
9377                 case "b":
9378                 case "bottom":
9379                 case "down":
9380                      if(h - t > ch){
9381                          var v = Math.min(t + distance, h-ch);
9382                          this.scrollTo("top", v, a);
9383                          scrolled = true;
9384                      }
9385                      break;
9386              }
9387              return scrolled;
9388         },
9389
9390         /**
9391          * Translates the passed page coordinates into left/top css values for this element
9392          * @param {Number/Array} x The page x or an array containing [x, y]
9393          * @param {Number} y The page y
9394          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9395          */
9396         translatePoints : function(x, y){
9397             if(typeof x == 'object' || x instanceof Array){
9398                 y = x[1]; x = x[0];
9399             }
9400             var p = this.getStyle('position');
9401             var o = this.getXY();
9402
9403             var l = parseInt(this.getStyle('left'), 10);
9404             var t = parseInt(this.getStyle('top'), 10);
9405
9406             if(isNaN(l)){
9407                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9408             }
9409             if(isNaN(t)){
9410                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9411             }
9412
9413             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9414         },
9415
9416         /**
9417          * Returns the current scroll position of the element.
9418          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9419          */
9420         getScroll : function(){
9421             var d = this.dom, doc = document;
9422             if(d == doc || d == doc.body){
9423                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9424                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9425                 return {left: l, top: t};
9426             }else{
9427                 return {left: d.scrollLeft, top: d.scrollTop};
9428             }
9429         },
9430
9431         /**
9432          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9433          * are convert to standard 6 digit hex color.
9434          * @param {String} attr The css attribute
9435          * @param {String} defaultValue The default value to use when a valid color isn't found
9436          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9437          * YUI color anims.
9438          */
9439         getColor : function(attr, defaultValue, prefix){
9440             var v = this.getStyle(attr);
9441             if(!v || v == "transparent" || v == "inherit") {
9442                 return defaultValue;
9443             }
9444             var color = typeof prefix == "undefined" ? "#" : prefix;
9445             if(v.substr(0, 4) == "rgb("){
9446                 var rvs = v.slice(4, v.length -1).split(",");
9447                 for(var i = 0; i < 3; i++){
9448                     var h = parseInt(rvs[i]).toString(16);
9449                     if(h < 16){
9450                         h = "0" + h;
9451                     }
9452                     color += h;
9453                 }
9454             } else {
9455                 if(v.substr(0, 1) == "#"){
9456                     if(v.length == 4) {
9457                         for(var i = 1; i < 4; i++){
9458                             var c = v.charAt(i);
9459                             color +=  c + c;
9460                         }
9461                     }else if(v.length == 7){
9462                         color += v.substr(1);
9463                     }
9464                 }
9465             }
9466             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9467         },
9468
9469         /**
9470          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9471          * gradient background, rounded corners and a 4-way shadow.
9472          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9473          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9474          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9475          * @return {Roo.Element} this
9476          */
9477         boxWrap : function(cls){
9478             cls = cls || 'x-box';
9479             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9480             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9481             return el;
9482         },
9483
9484         /**
9485          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9486          * @param {String} namespace The namespace in which to look for the attribute
9487          * @param {String} name The attribute name
9488          * @return {String} The attribute value
9489          */
9490         getAttributeNS : Roo.isIE ? function(ns, name){
9491             var d = this.dom;
9492             var type = typeof d[ns+":"+name];
9493             if(type != 'undefined' && type != 'unknown'){
9494                 return d[ns+":"+name];
9495             }
9496             return d[name];
9497         } : function(ns, name){
9498             var d = this.dom;
9499             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9500         }
9501     };
9502
9503     var ep = El.prototype;
9504
9505     /**
9506      * Appends an event handler (Shorthand for addListener)
9507      * @param {String}   eventName     The type of event to append
9508      * @param {Function} fn        The method the event invokes
9509      * @param {Object} scope       (optional) The scope (this object) of the fn
9510      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9511      * @method
9512      */
9513     ep.on = ep.addListener;
9514         // backwards compat
9515     ep.mon = ep.addListener;
9516
9517     /**
9518      * Removes an event handler from this element (shorthand for removeListener)
9519      * @param {String} eventName the type of event to remove
9520      * @param {Function} fn the method the event invokes
9521      * @return {Roo.Element} this
9522      * @method
9523      */
9524     ep.un = ep.removeListener;
9525
9526     /**
9527      * true to automatically adjust width and height settings for box-model issues (default to true)
9528      */
9529     ep.autoBoxAdjust = true;
9530
9531     // private
9532     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9533
9534     // private
9535     El.addUnits = function(v, defaultUnit){
9536         if(v === "" || v == "auto"){
9537             return v;
9538         }
9539         if(v === undefined){
9540             return '';
9541         }
9542         if(typeof v == "number" || !El.unitPattern.test(v)){
9543             return v + (defaultUnit || 'px');
9544         }
9545         return v;
9546     };
9547
9548     // special markup used throughout Roo when box wrapping elements
9549     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>';
9550     /**
9551      * Visibility mode constant - Use visibility to hide element
9552      * @static
9553      * @type Number
9554      */
9555     El.VISIBILITY = 1;
9556     /**
9557      * Visibility mode constant - Use display to hide element
9558      * @static
9559      * @type Number
9560      */
9561     El.DISPLAY = 2;
9562
9563     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9564     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9565     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9566
9567
9568
9569     /**
9570      * @private
9571      */
9572     El.cache = {};
9573
9574     var docEl;
9575
9576     /**
9577      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9578      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9579      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9580      * @return {Element} The Element object
9581      * @static
9582      */
9583     El.get = function(el){
9584         var ex, elm, id;
9585         if(!el){ return null; }
9586         if(typeof el == "string"){ // element id
9587             if(!(elm = document.getElementById(el))){
9588                 return null;
9589             }
9590             if(ex = El.cache[el]){
9591                 ex.dom = elm;
9592             }else{
9593                 ex = El.cache[el] = new El(elm);
9594             }
9595             return ex;
9596         }else if(el.tagName){ // dom element
9597             if(!(id = el.id)){
9598                 id = Roo.id(el);
9599             }
9600             if(ex = El.cache[id]){
9601                 ex.dom = el;
9602             }else{
9603                 ex = El.cache[id] = new El(el);
9604             }
9605             return ex;
9606         }else if(el instanceof El){
9607             if(el != docEl){
9608                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9609                                                               // catch case where it hasn't been appended
9610                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9611             }
9612             return el;
9613         }else if(el.isComposite){
9614             return el;
9615         }else if(el instanceof Array){
9616             return El.select(el);
9617         }else if(el == document){
9618             // create a bogus element object representing the document object
9619             if(!docEl){
9620                 var f = function(){};
9621                 f.prototype = El.prototype;
9622                 docEl = new f();
9623                 docEl.dom = document;
9624             }
9625             return docEl;
9626         }
9627         return null;
9628     };
9629
9630     // private
9631     El.uncache = function(el){
9632         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9633             if(a[i]){
9634                 delete El.cache[a[i].id || a[i]];
9635             }
9636         }
9637     };
9638
9639     // private
9640     // Garbage collection - uncache elements/purge listeners on orphaned elements
9641     // so we don't hold a reference and cause the browser to retain them
9642     El.garbageCollect = function(){
9643         if(!Roo.enableGarbageCollector){
9644             clearInterval(El.collectorThread);
9645             return;
9646         }
9647         for(var eid in El.cache){
9648             var el = El.cache[eid], d = el.dom;
9649             // -------------------------------------------------------
9650             // Determining what is garbage:
9651             // -------------------------------------------------------
9652             // !d
9653             // dom node is null, definitely garbage
9654             // -------------------------------------------------------
9655             // !d.parentNode
9656             // no parentNode == direct orphan, definitely garbage
9657             // -------------------------------------------------------
9658             // !d.offsetParent && !document.getElementById(eid)
9659             // display none elements have no offsetParent so we will
9660             // also try to look it up by it's id. However, check
9661             // offsetParent first so we don't do unneeded lookups.
9662             // This enables collection of elements that are not orphans
9663             // directly, but somewhere up the line they have an orphan
9664             // parent.
9665             // -------------------------------------------------------
9666             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9667                 delete El.cache[eid];
9668                 if(d && Roo.enableListenerCollection){
9669                     E.purgeElement(d);
9670                 }
9671             }
9672         }
9673     }
9674     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9675
9676
9677     // dom is optional
9678     El.Flyweight = function(dom){
9679         this.dom = dom;
9680     };
9681     El.Flyweight.prototype = El.prototype;
9682
9683     El._flyweights = {};
9684     /**
9685      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9686      * the dom node can be overwritten by other code.
9687      * @param {String/HTMLElement} el The dom node or id
9688      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9689      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9690      * @static
9691      * @return {Element} The shared Element object
9692      */
9693     El.fly = function(el, named){
9694         named = named || '_global';
9695         el = Roo.getDom(el);
9696         if(!el){
9697             return null;
9698         }
9699         if(!El._flyweights[named]){
9700             El._flyweights[named] = new El.Flyweight();
9701         }
9702         El._flyweights[named].dom = el;
9703         return El._flyweights[named];
9704     };
9705
9706     /**
9707      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9708      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9709      * Shorthand of {@link Roo.Element#get}
9710      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9711      * @return {Element} The Element object
9712      * @member Roo
9713      * @method get
9714      */
9715     Roo.get = El.get;
9716     /**
9717      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9718      * the dom node can be overwritten by other code.
9719      * Shorthand of {@link Roo.Element#fly}
9720      * @param {String/HTMLElement} el The dom node or id
9721      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9722      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9723      * @static
9724      * @return {Element} The shared Element object
9725      * @member Roo
9726      * @method fly
9727      */
9728     Roo.fly = El.fly;
9729
9730     // speedy lookup for elements never to box adjust
9731     var noBoxAdjust = Roo.isStrict ? {
9732         select:1
9733     } : {
9734         input:1, select:1, textarea:1
9735     };
9736     if(Roo.isIE || Roo.isGecko){
9737         noBoxAdjust['button'] = 1;
9738     }
9739
9740
9741     Roo.EventManager.on(window, 'unload', function(){
9742         delete El.cache;
9743         delete El._flyweights;
9744     });
9745 })();
9746
9747
9748
9749
9750 if(Roo.DomQuery){
9751     Roo.Element.selectorFunction = Roo.DomQuery.select;
9752 }
9753
9754 Roo.Element.select = function(selector, unique, root){
9755     var els;
9756     if(typeof selector == "string"){
9757         els = Roo.Element.selectorFunction(selector, root);
9758     }else if(selector.length !== undefined){
9759         els = selector;
9760     }else{
9761         throw "Invalid selector";
9762     }
9763     if(unique === true){
9764         return new Roo.CompositeElement(els);
9765     }else{
9766         return new Roo.CompositeElementLite(els);
9767     }
9768 };
9769 /**
9770  * Selects elements based on the passed CSS selector to enable working on them as 1.
9771  * @param {String/Array} selector The CSS selector or an array of elements
9772  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9773  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9774  * @return {CompositeElementLite/CompositeElement}
9775  * @member Roo
9776  * @method select
9777  */
9778 Roo.select = Roo.Element.select;
9779
9780
9781
9782
9783
9784
9785
9786
9787
9788
9789
9790
9791
9792
9793 /*
9794  * Based on:
9795  * Ext JS Library 1.1.1
9796  * Copyright(c) 2006-2007, Ext JS, LLC.
9797  *
9798  * Originally Released Under LGPL - original licence link has changed is not relivant.
9799  *
9800  * Fork - LGPL
9801  * <script type="text/javascript">
9802  */
9803
9804
9805
9806 //Notifies Element that fx methods are available
9807 Roo.enableFx = true;
9808
9809 /**
9810  * @class Roo.Fx
9811  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9812  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9813  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9814  * Element effects to work.</p><br/>
9815  *
9816  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9817  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9818  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9819  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9820  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9821  * expected results and should be done with care.</p><br/>
9822  *
9823  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9824  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9825 <pre>
9826 Value  Description
9827 -----  -----------------------------
9828 tl     The top left corner
9829 t      The center of the top edge
9830 tr     The top right corner
9831 l      The center of the left edge
9832 r      The center of the right edge
9833 bl     The bottom left corner
9834 b      The center of the bottom edge
9835 br     The bottom right corner
9836 </pre>
9837  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9838  * below are common options that can be passed to any Fx method.</b>
9839  * @cfg {Function} callback A function called when the effect is finished
9840  * @cfg {Object} scope The scope of the effect function
9841  * @cfg {String} easing A valid Easing value for the effect
9842  * @cfg {String} afterCls A css class to apply after the effect
9843  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9844  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9845  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9846  * effects that end with the element being visually hidden, ignored otherwise)
9847  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9848  * a function which returns such a specification that will be applied to the Element after the effect finishes
9849  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9850  * @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
9851  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9852  */
9853 Roo.Fx = {
9854         /**
9855          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9856          * origin for the slide effect.  This function automatically handles wrapping the element with
9857          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9858          * Usage:
9859          *<pre><code>
9860 // default: slide the element in from the top
9861 el.slideIn();
9862
9863 // custom: slide the element in from the right with a 2-second duration
9864 el.slideIn('r', { duration: 2 });
9865
9866 // common config options shown with default values
9867 el.slideIn('t', {
9868     easing: 'easeOut',
9869     duration: .5
9870 });
9871 </code></pre>
9872          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9873          * @param {Object} options (optional) Object literal with any of the Fx config options
9874          * @return {Roo.Element} The Element
9875          */
9876     slideIn : function(anchor, o){
9877         var el = this.getFxEl();
9878         o = o || {};
9879
9880         el.queueFx(o, function(){
9881
9882             anchor = anchor || "t";
9883
9884             // fix display to visibility
9885             this.fixDisplay();
9886
9887             // restore values after effect
9888             var r = this.getFxRestore();
9889             var b = this.getBox();
9890             // fixed size for slide
9891             this.setSize(b);
9892
9893             // wrap if needed
9894             var wrap = this.fxWrap(r.pos, o, "hidden");
9895
9896             var st = this.dom.style;
9897             st.visibility = "visible";
9898             st.position = "absolute";
9899
9900             // clear out temp styles after slide and unwrap
9901             var after = function(){
9902                 el.fxUnwrap(wrap, r.pos, o);
9903                 st.width = r.width;
9904                 st.height = r.height;
9905                 el.afterFx(o);
9906             };
9907             // time to calc the positions
9908             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9909
9910             switch(anchor.toLowerCase()){
9911                 case "t":
9912                     wrap.setSize(b.width, 0);
9913                     st.left = st.bottom = "0";
9914                     a = {height: bh};
9915                 break;
9916                 case "l":
9917                     wrap.setSize(0, b.height);
9918                     st.right = st.top = "0";
9919                     a = {width: bw};
9920                 break;
9921                 case "r":
9922                     wrap.setSize(0, b.height);
9923                     wrap.setX(b.right);
9924                     st.left = st.top = "0";
9925                     a = {width: bw, points: pt};
9926                 break;
9927                 case "b":
9928                     wrap.setSize(b.width, 0);
9929                     wrap.setY(b.bottom);
9930                     st.left = st.top = "0";
9931                     a = {height: bh, points: pt};
9932                 break;
9933                 case "tl":
9934                     wrap.setSize(0, 0);
9935                     st.right = st.bottom = "0";
9936                     a = {width: bw, height: bh};
9937                 break;
9938                 case "bl":
9939                     wrap.setSize(0, 0);
9940                     wrap.setY(b.y+b.height);
9941                     st.right = st.top = "0";
9942                     a = {width: bw, height: bh, points: pt};
9943                 break;
9944                 case "br":
9945                     wrap.setSize(0, 0);
9946                     wrap.setXY([b.right, b.bottom]);
9947                     st.left = st.top = "0";
9948                     a = {width: bw, height: bh, points: pt};
9949                 break;
9950                 case "tr":
9951                     wrap.setSize(0, 0);
9952                     wrap.setX(b.x+b.width);
9953                     st.left = st.bottom = "0";
9954                     a = {width: bw, height: bh, points: pt};
9955                 break;
9956             }
9957             this.dom.style.visibility = "visible";
9958             wrap.show();
9959
9960             arguments.callee.anim = wrap.fxanim(a,
9961                 o,
9962                 'motion',
9963                 .5,
9964                 'easeOut', after);
9965         });
9966         return this;
9967     },
9968     
9969         /**
9970          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9971          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9972          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9973          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9974          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9975          * Usage:
9976          *<pre><code>
9977 // default: slide the element out to the top
9978 el.slideOut();
9979
9980 // custom: slide the element out to the right with a 2-second duration
9981 el.slideOut('r', { duration: 2 });
9982
9983 // common config options shown with default values
9984 el.slideOut('t', {
9985     easing: 'easeOut',
9986     duration: .5,
9987     remove: false,
9988     useDisplay: false
9989 });
9990 </code></pre>
9991          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9992          * @param {Object} options (optional) Object literal with any of the Fx config options
9993          * @return {Roo.Element} The Element
9994          */
9995     slideOut : function(anchor, o){
9996         var el = this.getFxEl();
9997         o = o || {};
9998
9999         el.queueFx(o, function(){
10000
10001             anchor = anchor || "t";
10002
10003             // restore values after effect
10004             var r = this.getFxRestore();
10005             
10006             var b = this.getBox();
10007             // fixed size for slide
10008             this.setSize(b);
10009
10010             // wrap if needed
10011             var wrap = this.fxWrap(r.pos, o, "visible");
10012
10013             var st = this.dom.style;
10014             st.visibility = "visible";
10015             st.position = "absolute";
10016
10017             wrap.setSize(b);
10018
10019             var after = function(){
10020                 if(o.useDisplay){
10021                     el.setDisplayed(false);
10022                 }else{
10023                     el.hide();
10024                 }
10025
10026                 el.fxUnwrap(wrap, r.pos, o);
10027
10028                 st.width = r.width;
10029                 st.height = r.height;
10030
10031                 el.afterFx(o);
10032             };
10033
10034             var a, zero = {to: 0};
10035             switch(anchor.toLowerCase()){
10036                 case "t":
10037                     st.left = st.bottom = "0";
10038                     a = {height: zero};
10039                 break;
10040                 case "l":
10041                     st.right = st.top = "0";
10042                     a = {width: zero};
10043                 break;
10044                 case "r":
10045                     st.left = st.top = "0";
10046                     a = {width: zero, points: {to:[b.right, b.y]}};
10047                 break;
10048                 case "b":
10049                     st.left = st.top = "0";
10050                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10051                 break;
10052                 case "tl":
10053                     st.right = st.bottom = "0";
10054                     a = {width: zero, height: zero};
10055                 break;
10056                 case "bl":
10057                     st.right = st.top = "0";
10058                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10059                 break;
10060                 case "br":
10061                     st.left = st.top = "0";
10062                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10063                 break;
10064                 case "tr":
10065                     st.left = st.bottom = "0";
10066                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10067                 break;
10068             }
10069
10070             arguments.callee.anim = wrap.fxanim(a,
10071                 o,
10072                 'motion',
10073                 .5,
10074                 "easeOut", after);
10075         });
10076         return this;
10077     },
10078
10079         /**
10080          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10081          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10082          * The element must be removed from the DOM using the 'remove' config option if desired.
10083          * Usage:
10084          *<pre><code>
10085 // default
10086 el.puff();
10087
10088 // common config options shown with default values
10089 el.puff({
10090     easing: 'easeOut',
10091     duration: .5,
10092     remove: false,
10093     useDisplay: false
10094 });
10095 </code></pre>
10096          * @param {Object} options (optional) Object literal with any of the Fx config options
10097          * @return {Roo.Element} The Element
10098          */
10099     puff : function(o){
10100         var el = this.getFxEl();
10101         o = o || {};
10102
10103         el.queueFx(o, function(){
10104             this.clearOpacity();
10105             this.show();
10106
10107             // restore values after effect
10108             var r = this.getFxRestore();
10109             var st = this.dom.style;
10110
10111             var after = function(){
10112                 if(o.useDisplay){
10113                     el.setDisplayed(false);
10114                 }else{
10115                     el.hide();
10116                 }
10117
10118                 el.clearOpacity();
10119
10120                 el.setPositioning(r.pos);
10121                 st.width = r.width;
10122                 st.height = r.height;
10123                 st.fontSize = '';
10124                 el.afterFx(o);
10125             };
10126
10127             var width = this.getWidth();
10128             var height = this.getHeight();
10129
10130             arguments.callee.anim = this.fxanim({
10131                     width : {to: this.adjustWidth(width * 2)},
10132                     height : {to: this.adjustHeight(height * 2)},
10133                     points : {by: [-(width * .5), -(height * .5)]},
10134                     opacity : {to: 0},
10135                     fontSize: {to:200, unit: "%"}
10136                 },
10137                 o,
10138                 'motion',
10139                 .5,
10140                 "easeOut", after);
10141         });
10142         return this;
10143     },
10144
10145         /**
10146          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10147          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10148          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10149          * Usage:
10150          *<pre><code>
10151 // default
10152 el.switchOff();
10153
10154 // all config options shown with default values
10155 el.switchOff({
10156     easing: 'easeIn',
10157     duration: .3,
10158     remove: false,
10159     useDisplay: false
10160 });
10161 </code></pre>
10162          * @param {Object} options (optional) Object literal with any of the Fx config options
10163          * @return {Roo.Element} The Element
10164          */
10165     switchOff : function(o){
10166         var el = this.getFxEl();
10167         o = o || {};
10168
10169         el.queueFx(o, function(){
10170             this.clearOpacity();
10171             this.clip();
10172
10173             // restore values after effect
10174             var r = this.getFxRestore();
10175             var st = this.dom.style;
10176
10177             var after = function(){
10178                 if(o.useDisplay){
10179                     el.setDisplayed(false);
10180                 }else{
10181                     el.hide();
10182                 }
10183
10184                 el.clearOpacity();
10185                 el.setPositioning(r.pos);
10186                 st.width = r.width;
10187                 st.height = r.height;
10188
10189                 el.afterFx(o);
10190             };
10191
10192             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10193                 this.clearOpacity();
10194                 (function(){
10195                     this.fxanim({
10196                         height:{to:1},
10197                         points:{by:[0, this.getHeight() * .5]}
10198                     }, o, 'motion', 0.3, 'easeIn', after);
10199                 }).defer(100, this);
10200             });
10201         });
10202         return this;
10203     },
10204
10205     /**
10206      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10207      * changed using the "attr" config option) and then fading back to the original color. If no original
10208      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10209      * Usage:
10210 <pre><code>
10211 // default: highlight background to yellow
10212 el.highlight();
10213
10214 // custom: highlight foreground text to blue for 2 seconds
10215 el.highlight("0000ff", { attr: 'color', duration: 2 });
10216
10217 // common config options shown with default values
10218 el.highlight("ffff9c", {
10219     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10220     endColor: (current color) or "ffffff",
10221     easing: 'easeIn',
10222     duration: 1
10223 });
10224 </code></pre>
10225      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10226      * @param {Object} options (optional) Object literal with any of the Fx config options
10227      * @return {Roo.Element} The Element
10228      */ 
10229     highlight : function(color, o){
10230         var el = this.getFxEl();
10231         o = o || {};
10232
10233         el.queueFx(o, function(){
10234             color = color || "ffff9c";
10235             attr = o.attr || "backgroundColor";
10236
10237             this.clearOpacity();
10238             this.show();
10239
10240             var origColor = this.getColor(attr);
10241             var restoreColor = this.dom.style[attr];
10242             endColor = (o.endColor || origColor) || "ffffff";
10243
10244             var after = function(){
10245                 el.dom.style[attr] = restoreColor;
10246                 el.afterFx(o);
10247             };
10248
10249             var a = {};
10250             a[attr] = {from: color, to: endColor};
10251             arguments.callee.anim = this.fxanim(a,
10252                 o,
10253                 'color',
10254                 1,
10255                 'easeIn', after);
10256         });
10257         return this;
10258     },
10259
10260    /**
10261     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10262     * Usage:
10263 <pre><code>
10264 // default: a single light blue ripple
10265 el.frame();
10266
10267 // custom: 3 red ripples lasting 3 seconds total
10268 el.frame("ff0000", 3, { duration: 3 });
10269
10270 // common config options shown with default values
10271 el.frame("C3DAF9", 1, {
10272     duration: 1 //duration of entire animation (not each individual ripple)
10273     // Note: Easing is not configurable and will be ignored if included
10274 });
10275 </code></pre>
10276     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10277     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10278     * @param {Object} options (optional) Object literal with any of the Fx config options
10279     * @return {Roo.Element} The Element
10280     */
10281     frame : function(color, count, o){
10282         var el = this.getFxEl();
10283         o = o || {};
10284
10285         el.queueFx(o, function(){
10286             color = color || "#C3DAF9";
10287             if(color.length == 6){
10288                 color = "#" + color;
10289             }
10290             count = count || 1;
10291             duration = o.duration || 1;
10292             this.show();
10293
10294             var b = this.getBox();
10295             var animFn = function(){
10296                 var proxy = this.createProxy({
10297
10298                      style:{
10299                         visbility:"hidden",
10300                         position:"absolute",
10301                         "z-index":"35000", // yee haw
10302                         border:"0px solid " + color
10303                      }
10304                   });
10305                 var scale = Roo.isBorderBox ? 2 : 1;
10306                 proxy.animate({
10307                     top:{from:b.y, to:b.y - 20},
10308                     left:{from:b.x, to:b.x - 20},
10309                     borderWidth:{from:0, to:10},
10310                     opacity:{from:1, to:0},
10311                     height:{from:b.height, to:(b.height + (20*scale))},
10312                     width:{from:b.width, to:(b.width + (20*scale))}
10313                 }, duration, function(){
10314                     proxy.remove();
10315                 });
10316                 if(--count > 0){
10317                      animFn.defer((duration/2)*1000, this);
10318                 }else{
10319                     el.afterFx(o);
10320                 }
10321             };
10322             animFn.call(this);
10323         });
10324         return this;
10325     },
10326
10327    /**
10328     * Creates a pause before any subsequent queued effects begin.  If there are
10329     * no effects queued after the pause it will have no effect.
10330     * Usage:
10331 <pre><code>
10332 el.pause(1);
10333 </code></pre>
10334     * @param {Number} seconds The length of time to pause (in seconds)
10335     * @return {Roo.Element} The Element
10336     */
10337     pause : function(seconds){
10338         var el = this.getFxEl();
10339         var o = {};
10340
10341         el.queueFx(o, function(){
10342             setTimeout(function(){
10343                 el.afterFx(o);
10344             }, seconds * 1000);
10345         });
10346         return this;
10347     },
10348
10349    /**
10350     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10351     * using the "endOpacity" config option.
10352     * Usage:
10353 <pre><code>
10354 // default: fade in from opacity 0 to 100%
10355 el.fadeIn();
10356
10357 // custom: fade in from opacity 0 to 75% over 2 seconds
10358 el.fadeIn({ endOpacity: .75, duration: 2});
10359
10360 // common config options shown with default values
10361 el.fadeIn({
10362     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10363     easing: 'easeOut',
10364     duration: .5
10365 });
10366 </code></pre>
10367     * @param {Object} options (optional) Object literal with any of the Fx config options
10368     * @return {Roo.Element} The Element
10369     */
10370     fadeIn : function(o){
10371         var el = this.getFxEl();
10372         o = o || {};
10373         el.queueFx(o, function(){
10374             this.setOpacity(0);
10375             this.fixDisplay();
10376             this.dom.style.visibility = 'visible';
10377             var to = o.endOpacity || 1;
10378             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10379                 o, null, .5, "easeOut", function(){
10380                 if(to == 1){
10381                     this.clearOpacity();
10382                 }
10383                 el.afterFx(o);
10384             });
10385         });
10386         return this;
10387     },
10388
10389    /**
10390     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10391     * using the "endOpacity" config option.
10392     * Usage:
10393 <pre><code>
10394 // default: fade out from the element's current opacity to 0
10395 el.fadeOut();
10396
10397 // custom: fade out from the element's current opacity to 25% over 2 seconds
10398 el.fadeOut({ endOpacity: .25, duration: 2});
10399
10400 // common config options shown with default values
10401 el.fadeOut({
10402     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10403     easing: 'easeOut',
10404     duration: .5
10405     remove: false,
10406     useDisplay: false
10407 });
10408 </code></pre>
10409     * @param {Object} options (optional) Object literal with any of the Fx config options
10410     * @return {Roo.Element} The Element
10411     */
10412     fadeOut : function(o){
10413         var el = this.getFxEl();
10414         o = o || {};
10415         el.queueFx(o, function(){
10416             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10417                 o, null, .5, "easeOut", function(){
10418                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10419                      this.dom.style.display = "none";
10420                 }else{
10421                      this.dom.style.visibility = "hidden";
10422                 }
10423                 this.clearOpacity();
10424                 el.afterFx(o);
10425             });
10426         });
10427         return this;
10428     },
10429
10430    /**
10431     * Animates the transition of an element's dimensions from a starting height/width
10432     * to an ending height/width.
10433     * Usage:
10434 <pre><code>
10435 // change height and width to 100x100 pixels
10436 el.scale(100, 100);
10437
10438 // common config options shown with default values.  The height and width will default to
10439 // the element's existing values if passed as null.
10440 el.scale(
10441     [element's width],
10442     [element's height], {
10443     easing: 'easeOut',
10444     duration: .35
10445 });
10446 </code></pre>
10447     * @param {Number} width  The new width (pass undefined to keep the original width)
10448     * @param {Number} height  The new height (pass undefined to keep the original height)
10449     * @param {Object} options (optional) Object literal with any of the Fx config options
10450     * @return {Roo.Element} The Element
10451     */
10452     scale : function(w, h, o){
10453         this.shift(Roo.apply({}, o, {
10454             width: w,
10455             height: h
10456         }));
10457         return this;
10458     },
10459
10460    /**
10461     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10462     * Any of these properties not specified in the config object will not be changed.  This effect 
10463     * requires that at least one new dimension, position or opacity setting must be passed in on
10464     * the config object in order for the function to have any effect.
10465     * Usage:
10466 <pre><code>
10467 // slide the element horizontally to x position 200 while changing the height and opacity
10468 el.shift({ x: 200, height: 50, opacity: .8 });
10469
10470 // common config options shown with default values.
10471 el.shift({
10472     width: [element's width],
10473     height: [element's height],
10474     x: [element's x position],
10475     y: [element's y position],
10476     opacity: [element's opacity],
10477     easing: 'easeOut',
10478     duration: .35
10479 });
10480 </code></pre>
10481     * @param {Object} options  Object literal with any of the Fx config options
10482     * @return {Roo.Element} The Element
10483     */
10484     shift : function(o){
10485         var el = this.getFxEl();
10486         o = o || {};
10487         el.queueFx(o, function(){
10488             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10489             if(w !== undefined){
10490                 a.width = {to: this.adjustWidth(w)};
10491             }
10492             if(h !== undefined){
10493                 a.height = {to: this.adjustHeight(h)};
10494             }
10495             if(x !== undefined || y !== undefined){
10496                 a.points = {to: [
10497                     x !== undefined ? x : this.getX(),
10498                     y !== undefined ? y : this.getY()
10499                 ]};
10500             }
10501             if(op !== undefined){
10502                 a.opacity = {to: op};
10503             }
10504             if(o.xy !== undefined){
10505                 a.points = {to: o.xy};
10506             }
10507             arguments.callee.anim = this.fxanim(a,
10508                 o, 'motion', .35, "easeOut", function(){
10509                 el.afterFx(o);
10510             });
10511         });
10512         return this;
10513     },
10514
10515         /**
10516          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10517          * ending point of the effect.
10518          * Usage:
10519          *<pre><code>
10520 // default: slide the element downward while fading out
10521 el.ghost();
10522
10523 // custom: slide the element out to the right with a 2-second duration
10524 el.ghost('r', { duration: 2 });
10525
10526 // common config options shown with default values
10527 el.ghost('b', {
10528     easing: 'easeOut',
10529     duration: .5
10530     remove: false,
10531     useDisplay: false
10532 });
10533 </code></pre>
10534          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10535          * @param {Object} options (optional) Object literal with any of the Fx config options
10536          * @return {Roo.Element} The Element
10537          */
10538     ghost : function(anchor, o){
10539         var el = this.getFxEl();
10540         o = o || {};
10541
10542         el.queueFx(o, function(){
10543             anchor = anchor || "b";
10544
10545             // restore values after effect
10546             var r = this.getFxRestore();
10547             var w = this.getWidth(),
10548                 h = this.getHeight();
10549
10550             var st = this.dom.style;
10551
10552             var after = function(){
10553                 if(o.useDisplay){
10554                     el.setDisplayed(false);
10555                 }else{
10556                     el.hide();
10557                 }
10558
10559                 el.clearOpacity();
10560                 el.setPositioning(r.pos);
10561                 st.width = r.width;
10562                 st.height = r.height;
10563
10564                 el.afterFx(o);
10565             };
10566
10567             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10568             switch(anchor.toLowerCase()){
10569                 case "t":
10570                     pt.by = [0, -h];
10571                 break;
10572                 case "l":
10573                     pt.by = [-w, 0];
10574                 break;
10575                 case "r":
10576                     pt.by = [w, 0];
10577                 break;
10578                 case "b":
10579                     pt.by = [0, h];
10580                 break;
10581                 case "tl":
10582                     pt.by = [-w, -h];
10583                 break;
10584                 case "bl":
10585                     pt.by = [-w, h];
10586                 break;
10587                 case "br":
10588                     pt.by = [w, h];
10589                 break;
10590                 case "tr":
10591                     pt.by = [w, -h];
10592                 break;
10593             }
10594
10595             arguments.callee.anim = this.fxanim(a,
10596                 o,
10597                 'motion',
10598                 .5,
10599                 "easeOut", after);
10600         });
10601         return this;
10602     },
10603
10604         /**
10605          * Ensures that all effects queued after syncFx is called on the element are
10606          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10607          * @return {Roo.Element} The Element
10608          */
10609     syncFx : function(){
10610         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10611             block : false,
10612             concurrent : true,
10613             stopFx : false
10614         });
10615         return this;
10616     },
10617
10618         /**
10619          * Ensures that all effects queued after sequenceFx is called on the element are
10620          * run in sequence.  This is the opposite of {@link #syncFx}.
10621          * @return {Roo.Element} The Element
10622          */
10623     sequenceFx : function(){
10624         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10625             block : false,
10626             concurrent : false,
10627             stopFx : false
10628         });
10629         return this;
10630     },
10631
10632         /* @private */
10633     nextFx : function(){
10634         var ef = this.fxQueue[0];
10635         if(ef){
10636             ef.call(this);
10637         }
10638     },
10639
10640         /**
10641          * Returns true if the element has any effects actively running or queued, else returns false.
10642          * @return {Boolean} True if element has active effects, else false
10643          */
10644     hasActiveFx : function(){
10645         return this.fxQueue && this.fxQueue[0];
10646     },
10647
10648         /**
10649          * Stops any running effects and clears the element's internal effects queue if it contains
10650          * any additional effects that haven't started yet.
10651          * @return {Roo.Element} The Element
10652          */
10653     stopFx : function(){
10654         if(this.hasActiveFx()){
10655             var cur = this.fxQueue[0];
10656             if(cur && cur.anim && cur.anim.isAnimated()){
10657                 this.fxQueue = [cur]; // clear out others
10658                 cur.anim.stop(true);
10659             }
10660         }
10661         return this;
10662     },
10663
10664         /* @private */
10665     beforeFx : function(o){
10666         if(this.hasActiveFx() && !o.concurrent){
10667            if(o.stopFx){
10668                this.stopFx();
10669                return true;
10670            }
10671            return false;
10672         }
10673         return true;
10674     },
10675
10676         /**
10677          * Returns true if the element is currently blocking so that no other effect can be queued
10678          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10679          * used to ensure that an effect initiated by a user action runs to completion prior to the
10680          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10681          * @return {Boolean} True if blocking, else false
10682          */
10683     hasFxBlock : function(){
10684         var q = this.fxQueue;
10685         return q && q[0] && q[0].block;
10686     },
10687
10688         /* @private */
10689     queueFx : function(o, fn){
10690         if(!this.fxQueue){
10691             this.fxQueue = [];
10692         }
10693         if(!this.hasFxBlock()){
10694             Roo.applyIf(o, this.fxDefaults);
10695             if(!o.concurrent){
10696                 var run = this.beforeFx(o);
10697                 fn.block = o.block;
10698                 this.fxQueue.push(fn);
10699                 if(run){
10700                     this.nextFx();
10701                 }
10702             }else{
10703                 fn.call(this);
10704             }
10705         }
10706         return this;
10707     },
10708
10709         /* @private */
10710     fxWrap : function(pos, o, vis){
10711         var wrap;
10712         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10713             var wrapXY;
10714             if(o.fixPosition){
10715                 wrapXY = this.getXY();
10716             }
10717             var div = document.createElement("div");
10718             div.style.visibility = vis;
10719             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10720             wrap.setPositioning(pos);
10721             if(wrap.getStyle("position") == "static"){
10722                 wrap.position("relative");
10723             }
10724             this.clearPositioning('auto');
10725             wrap.clip();
10726             wrap.dom.appendChild(this.dom);
10727             if(wrapXY){
10728                 wrap.setXY(wrapXY);
10729             }
10730         }
10731         return wrap;
10732     },
10733
10734         /* @private */
10735     fxUnwrap : function(wrap, pos, o){
10736         this.clearPositioning();
10737         this.setPositioning(pos);
10738         if(!o.wrap){
10739             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10740             wrap.remove();
10741         }
10742     },
10743
10744         /* @private */
10745     getFxRestore : function(){
10746         var st = this.dom.style;
10747         return {pos: this.getPositioning(), width: st.width, height : st.height};
10748     },
10749
10750         /* @private */
10751     afterFx : function(o){
10752         if(o.afterStyle){
10753             this.applyStyles(o.afterStyle);
10754         }
10755         if(o.afterCls){
10756             this.addClass(o.afterCls);
10757         }
10758         if(o.remove === true){
10759             this.remove();
10760         }
10761         Roo.callback(o.callback, o.scope, [this]);
10762         if(!o.concurrent){
10763             this.fxQueue.shift();
10764             this.nextFx();
10765         }
10766     },
10767
10768         /* @private */
10769     getFxEl : function(){ // support for composite element fx
10770         return Roo.get(this.dom);
10771     },
10772
10773         /* @private */
10774     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10775         animType = animType || 'run';
10776         opt = opt || {};
10777         var anim = Roo.lib.Anim[animType](
10778             this.dom, args,
10779             (opt.duration || defaultDur) || .35,
10780             (opt.easing || defaultEase) || 'easeOut',
10781             function(){
10782                 Roo.callback(cb, this);
10783             },
10784             this
10785         );
10786         opt.anim = anim;
10787         return anim;
10788     }
10789 };
10790
10791 // backwords compat
10792 Roo.Fx.resize = Roo.Fx.scale;
10793
10794 //When included, Roo.Fx is automatically applied to Element so that all basic
10795 //effects are available directly via the Element API
10796 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10797  * Based on:
10798  * Ext JS Library 1.1.1
10799  * Copyright(c) 2006-2007, Ext JS, LLC.
10800  *
10801  * Originally Released Under LGPL - original licence link has changed is not relivant.
10802  *
10803  * Fork - LGPL
10804  * <script type="text/javascript">
10805  */
10806
10807
10808 /**
10809  * @class Roo.CompositeElement
10810  * Standard composite class. Creates a Roo.Element for every element in the collection.
10811  * <br><br>
10812  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10813  * actions will be performed on all the elements in this collection.</b>
10814  * <br><br>
10815  * All methods return <i>this</i> and can be chained.
10816  <pre><code>
10817  var els = Roo.select("#some-el div.some-class", true);
10818  // or select directly from an existing element
10819  var el = Roo.get('some-el');
10820  el.select('div.some-class', true);
10821
10822  els.setWidth(100); // all elements become 100 width
10823  els.hide(true); // all elements fade out and hide
10824  // or
10825  els.setWidth(100).hide(true);
10826  </code></pre>
10827  */
10828 Roo.CompositeElement = function(els){
10829     this.elements = [];
10830     this.addElements(els);
10831 };
10832 Roo.CompositeElement.prototype = {
10833     isComposite: true,
10834     addElements : function(els){
10835         if(!els) return this;
10836         if(typeof els == "string"){
10837             els = Roo.Element.selectorFunction(els);
10838         }
10839         var yels = this.elements;
10840         var index = yels.length-1;
10841         for(var i = 0, len = els.length; i < len; i++) {
10842                 yels[++index] = Roo.get(els[i]);
10843         }
10844         return this;
10845     },
10846
10847     /**
10848     * Clears this composite and adds the elements returned by the passed selector.
10849     * @param {String/Array} els A string CSS selector, an array of elements or an element
10850     * @return {CompositeElement} this
10851     */
10852     fill : function(els){
10853         this.elements = [];
10854         this.add(els);
10855         return this;
10856     },
10857
10858     /**
10859     * Filters this composite to only elements that match the passed selector.
10860     * @param {String} selector A string CSS selector
10861     * @return {CompositeElement} this
10862     */
10863     filter : function(selector){
10864         var els = [];
10865         this.each(function(el){
10866             if(el.is(selector)){
10867                 els[els.length] = el.dom;
10868             }
10869         });
10870         this.fill(els);
10871         return this;
10872     },
10873
10874     invoke : function(fn, args){
10875         var els = this.elements;
10876         for(var i = 0, len = els.length; i < len; i++) {
10877                 Roo.Element.prototype[fn].apply(els[i], args);
10878         }
10879         return this;
10880     },
10881     /**
10882     * Adds elements to this composite.
10883     * @param {String/Array} els A string CSS selector, an array of elements or an element
10884     * @return {CompositeElement} this
10885     */
10886     add : function(els){
10887         if(typeof els == "string"){
10888             this.addElements(Roo.Element.selectorFunction(els));
10889         }else if(els.length !== undefined){
10890             this.addElements(els);
10891         }else{
10892             this.addElements([els]);
10893         }
10894         return this;
10895     },
10896     /**
10897     * Calls the passed function passing (el, this, index) for each element in this composite.
10898     * @param {Function} fn The function to call
10899     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10900     * @return {CompositeElement} this
10901     */
10902     each : function(fn, scope){
10903         var els = this.elements;
10904         for(var i = 0, len = els.length; i < len; i++){
10905             if(fn.call(scope || els[i], els[i], this, i) === false) {
10906                 break;
10907             }
10908         }
10909         return this;
10910     },
10911
10912     /**
10913      * Returns the Element object at the specified index
10914      * @param {Number} index
10915      * @return {Roo.Element}
10916      */
10917     item : function(index){
10918         return this.elements[index] || null;
10919     },
10920
10921     /**
10922      * Returns the first Element
10923      * @return {Roo.Element}
10924      */
10925     first : function(){
10926         return this.item(0);
10927     },
10928
10929     /**
10930      * Returns the last Element
10931      * @return {Roo.Element}
10932      */
10933     last : function(){
10934         return this.item(this.elements.length-1);
10935     },
10936
10937     /**
10938      * Returns the number of elements in this composite
10939      * @return Number
10940      */
10941     getCount : function(){
10942         return this.elements.length;
10943     },
10944
10945     /**
10946      * Returns true if this composite contains the passed element
10947      * @return Boolean
10948      */
10949     contains : function(el){
10950         return this.indexOf(el) !== -1;
10951     },
10952
10953     /**
10954      * Returns true if this composite contains the passed element
10955      * @return Boolean
10956      */
10957     indexOf : function(el){
10958         return this.elements.indexOf(Roo.get(el));
10959     },
10960
10961
10962     /**
10963     * Removes the specified element(s).
10964     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10965     * or an array of any of those.
10966     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10967     * @return {CompositeElement} this
10968     */
10969     removeElement : function(el, removeDom){
10970         if(el instanceof Array){
10971             for(var i = 0, len = el.length; i < len; i++){
10972                 this.removeElement(el[i]);
10973             }
10974             return this;
10975         }
10976         var index = typeof el == 'number' ? el : this.indexOf(el);
10977         if(index !== -1){
10978             if(removeDom){
10979                 var d = this.elements[index];
10980                 if(d.dom){
10981                     d.remove();
10982                 }else{
10983                     d.parentNode.removeChild(d);
10984                 }
10985             }
10986             this.elements.splice(index, 1);
10987         }
10988         return this;
10989     },
10990
10991     /**
10992     * Replaces the specified element with the passed element.
10993     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10994     * to replace.
10995     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10996     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10997     * @return {CompositeElement} this
10998     */
10999     replaceElement : function(el, replacement, domReplace){
11000         var index = typeof el == 'number' ? el : this.indexOf(el);
11001         if(index !== -1){
11002             if(domReplace){
11003                 this.elements[index].replaceWith(replacement);
11004             }else{
11005                 this.elements.splice(index, 1, Roo.get(replacement))
11006             }
11007         }
11008         return this;
11009     },
11010
11011     /**
11012      * Removes all elements.
11013      */
11014     clear : function(){
11015         this.elements = [];
11016     }
11017 };
11018 (function(){
11019     Roo.CompositeElement.createCall = function(proto, fnName){
11020         if(!proto[fnName]){
11021             proto[fnName] = function(){
11022                 return this.invoke(fnName, arguments);
11023             };
11024         }
11025     };
11026     for(var fnName in Roo.Element.prototype){
11027         if(typeof Roo.Element.prototype[fnName] == "function"){
11028             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11029         }
11030     };
11031 })();
11032 /*
11033  * Based on:
11034  * Ext JS Library 1.1.1
11035  * Copyright(c) 2006-2007, Ext JS, LLC.
11036  *
11037  * Originally Released Under LGPL - original licence link has changed is not relivant.
11038  *
11039  * Fork - LGPL
11040  * <script type="text/javascript">
11041  */
11042
11043 /**
11044  * @class Roo.CompositeElementLite
11045  * @extends Roo.CompositeElement
11046  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11047  <pre><code>
11048  var els = Roo.select("#some-el div.some-class");
11049  // or select directly from an existing element
11050  var el = Roo.get('some-el');
11051  el.select('div.some-class');
11052
11053  els.setWidth(100); // all elements become 100 width
11054  els.hide(true); // all elements fade out and hide
11055  // or
11056  els.setWidth(100).hide(true);
11057  </code></pre><br><br>
11058  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11059  * actions will be performed on all the elements in this collection.</b>
11060  */
11061 Roo.CompositeElementLite = function(els){
11062     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11063     this.el = new Roo.Element.Flyweight();
11064 };
11065 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11066     addElements : function(els){
11067         if(els){
11068             if(els instanceof Array){
11069                 this.elements = this.elements.concat(els);
11070             }else{
11071                 var yels = this.elements;
11072                 var index = yels.length-1;
11073                 for(var i = 0, len = els.length; i < len; i++) {
11074                     yels[++index] = els[i];
11075                 }
11076             }
11077         }
11078         return this;
11079     },
11080     invoke : function(fn, args){
11081         var els = this.elements;
11082         var el = this.el;
11083         for(var i = 0, len = els.length; i < len; i++) {
11084             el.dom = els[i];
11085                 Roo.Element.prototype[fn].apply(el, args);
11086         }
11087         return this;
11088     },
11089     /**
11090      * Returns a flyweight Element of the dom element object at the specified index
11091      * @param {Number} index
11092      * @return {Roo.Element}
11093      */
11094     item : function(index){
11095         if(!this.elements[index]){
11096             return null;
11097         }
11098         this.el.dom = this.elements[index];
11099         return this.el;
11100     },
11101
11102     // fixes scope with flyweight
11103     addListener : function(eventName, handler, scope, opt){
11104         var els = this.elements;
11105         for(var i = 0, len = els.length; i < len; i++) {
11106             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11107         }
11108         return this;
11109     },
11110
11111     /**
11112     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11113     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11114     * a reference to the dom node, use el.dom.</b>
11115     * @param {Function} fn The function to call
11116     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11117     * @return {CompositeElement} this
11118     */
11119     each : function(fn, scope){
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                 if(fn.call(scope || el, el, this, i) === false){
11125                 break;
11126             }
11127         }
11128         return this;
11129     },
11130
11131     indexOf : function(el){
11132         return this.elements.indexOf(Roo.getDom(el));
11133     },
11134
11135     replaceElement : function(el, replacement, domReplace){
11136         var index = typeof el == 'number' ? el : this.indexOf(el);
11137         if(index !== -1){
11138             replacement = Roo.getDom(replacement);
11139             if(domReplace){
11140                 var d = this.elements[index];
11141                 d.parentNode.insertBefore(replacement, d);
11142                 d.parentNode.removeChild(d);
11143             }
11144             this.elements.splice(index, 1, replacement);
11145         }
11146         return this;
11147     }
11148 });
11149 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11150
11151 /*
11152  * Based on:
11153  * Ext JS Library 1.1.1
11154  * Copyright(c) 2006-2007, Ext JS, LLC.
11155  *
11156  * Originally Released Under LGPL - original licence link has changed is not relivant.
11157  *
11158  * Fork - LGPL
11159  * <script type="text/javascript">
11160  */
11161
11162  
11163
11164 /**
11165  * @class Roo.data.Connection
11166  * @extends Roo.util.Observable
11167  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11168  * either to a configured URL, or to a URL specified at request time.<br><br>
11169  * <p>
11170  * Requests made by this class are asynchronous, and will return immediately. No data from
11171  * the server will be available to the statement immediately following the {@link #request} call.
11172  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11173  * <p>
11174  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11175  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11176  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11177  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11178  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11179  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11180  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11181  * standard DOM methods.
11182  * @constructor
11183  * @param {Object} config a configuration object.
11184  */
11185 Roo.data.Connection = function(config){
11186     Roo.apply(this, config);
11187     this.addEvents({
11188         /**
11189          * @event beforerequest
11190          * Fires before a network request is made to retrieve a data object.
11191          * @param {Connection} conn This Connection object.
11192          * @param {Object} options The options config object passed to the {@link #request} method.
11193          */
11194         "beforerequest" : true,
11195         /**
11196          * @event requestcomplete
11197          * Fires if the request was successfully completed.
11198          * @param {Connection} conn This Connection object.
11199          * @param {Object} response The XHR object containing the response data.
11200          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11201          * @param {Object} options The options config object passed to the {@link #request} method.
11202          */
11203         "requestcomplete" : true,
11204         /**
11205          * @event requestexception
11206          * Fires if an error HTTP status was returned from the server.
11207          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11208          * @param {Connection} conn This Connection object.
11209          * @param {Object} response The XHR object containing the response data.
11210          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11211          * @param {Object} options The options config object passed to the {@link #request} method.
11212          */
11213         "requestexception" : true
11214     });
11215     Roo.data.Connection.superclass.constructor.call(this);
11216 };
11217
11218 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11219     /**
11220      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11221      */
11222     /**
11223      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11224      * extra parameters to each request made by this object. (defaults to undefined)
11225      */
11226     /**
11227      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11228      *  to each request made by this object. (defaults to undefined)
11229      */
11230     /**
11231      * @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)
11232      */
11233     /**
11234      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11235      */
11236     timeout : 30000,
11237     /**
11238      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11239      * @type Boolean
11240      */
11241     autoAbort:false,
11242
11243     /**
11244      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11245      * @type Boolean
11246      */
11247     disableCaching: true,
11248
11249     /**
11250      * Sends an HTTP request to a remote server.
11251      * @param {Object} options An object which may contain the following properties:<ul>
11252      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11253      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11254      * request, a url encoded string or a function to call to get either.</li>
11255      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11256      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11257      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11258      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11259      * <li>options {Object} The parameter to the request call.</li>
11260      * <li>success {Boolean} True if the request succeeded.</li>
11261      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11262      * </ul></li>
11263      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11264      * The callback is passed the following parameters:<ul>
11265      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11266      * <li>options {Object} The parameter to the request call.</li>
11267      * </ul></li>
11268      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11269      * The callback is passed the following parameters:<ul>
11270      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11271      * <li>options {Object} The parameter to the request call.</li>
11272      * </ul></li>
11273      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11274      * for the callback function. Defaults to the browser window.</li>
11275      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11276      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11277      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11278      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11279      * params for the post data. Any params will be appended to the URL.</li>
11280      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11281      * </ul>
11282      * @return {Number} transactionId
11283      */
11284     request : function(o){
11285         if(this.fireEvent("beforerequest", this, o) !== false){
11286             var p = o.params;
11287
11288             if(typeof p == "function"){
11289                 p = p.call(o.scope||window, o);
11290             }
11291             if(typeof p == "object"){
11292                 p = Roo.urlEncode(o.params);
11293             }
11294             if(this.extraParams){
11295                 var extras = Roo.urlEncode(this.extraParams);
11296                 p = p ? (p + '&' + extras) : extras;
11297             }
11298
11299             var url = o.url || this.url;
11300             if(typeof url == 'function'){
11301                 url = url.call(o.scope||window, o);
11302             }
11303
11304             if(o.form){
11305                 var form = Roo.getDom(o.form);
11306                 url = url || form.action;
11307
11308                 var enctype = form.getAttribute("enctype");
11309                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11310                     return this.doFormUpload(o, p, url);
11311                 }
11312                 var f = Roo.lib.Ajax.serializeForm(form);
11313                 p = p ? (p + '&' + f) : f;
11314             }
11315
11316             var hs = o.headers;
11317             if(this.defaultHeaders){
11318                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11319                 if(!o.headers){
11320                     o.headers = hs;
11321                 }
11322             }
11323
11324             var cb = {
11325                 success: this.handleResponse,
11326                 failure: this.handleFailure,
11327                 scope: this,
11328                 argument: {options: o},
11329                 timeout : this.timeout
11330             };
11331
11332             var method = o.method||this.method||(p ? "POST" : "GET");
11333
11334             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11335                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11336             }
11337
11338             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11339                 if(o.autoAbort){
11340                     this.abort();
11341                 }
11342             }else if(this.autoAbort !== false){
11343                 this.abort();
11344             }
11345
11346             if((method == 'GET' && p) || o.xmlData){
11347                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11348                 p = '';
11349             }
11350             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11351             return this.transId;
11352         }else{
11353             Roo.callback(o.callback, o.scope, [o, null, null]);
11354             return null;
11355         }
11356     },
11357
11358     /**
11359      * Determine whether this object has a request outstanding.
11360      * @param {Number} transactionId (Optional) defaults to the last transaction
11361      * @return {Boolean} True if there is an outstanding request.
11362      */
11363     isLoading : function(transId){
11364         if(transId){
11365             return Roo.lib.Ajax.isCallInProgress(transId);
11366         }else{
11367             return this.transId ? true : false;
11368         }
11369     },
11370
11371     /**
11372      * Aborts any outstanding request.
11373      * @param {Number} transactionId (Optional) defaults to the last transaction
11374      */
11375     abort : function(transId){
11376         if(transId || this.isLoading()){
11377             Roo.lib.Ajax.abort(transId || this.transId);
11378         }
11379     },
11380
11381     // private
11382     handleResponse : function(response){
11383         this.transId = false;
11384         var options = response.argument.options;
11385         response.argument = options ? options.argument : null;
11386         this.fireEvent("requestcomplete", this, response, options);
11387         Roo.callback(options.success, options.scope, [response, options]);
11388         Roo.callback(options.callback, options.scope, [options, true, response]);
11389     },
11390
11391     // private
11392     handleFailure : function(response, e){
11393         this.transId = false;
11394         var options = response.argument.options;
11395         response.argument = options ? options.argument : null;
11396         this.fireEvent("requestexception", this, response, options, e);
11397         Roo.callback(options.failure, options.scope, [response, options]);
11398         Roo.callback(options.callback, options.scope, [options, false, response]);
11399     },
11400
11401     // private
11402     doFormUpload : function(o, ps, url){
11403         var id = Roo.id();
11404         var frame = document.createElement('iframe');
11405         frame.id = id;
11406         frame.name = id;
11407         frame.className = 'x-hidden';
11408         if(Roo.isIE){
11409             frame.src = Roo.SSL_SECURE_URL;
11410         }
11411         document.body.appendChild(frame);
11412
11413         if(Roo.isIE){
11414            document.frames[id].name = id;
11415         }
11416
11417         var form = Roo.getDom(o.form);
11418         form.target = id;
11419         form.method = 'POST';
11420         form.enctype = form.encoding = 'multipart/form-data';
11421         if(url){
11422             form.action = url;
11423         }
11424
11425         var hiddens, hd;
11426         if(ps){ // add dynamic params
11427             hiddens = [];
11428             ps = Roo.urlDecode(ps, false);
11429             for(var k in ps){
11430                 if(ps.hasOwnProperty(k)){
11431                     hd = document.createElement('input');
11432                     hd.type = 'hidden';
11433                     hd.name = k;
11434                     hd.value = ps[k];
11435                     form.appendChild(hd);
11436                     hiddens.push(hd);
11437                 }
11438             }
11439         }
11440
11441         function cb(){
11442             var r = {  // bogus response object
11443                 responseText : '',
11444                 responseXML : null
11445             };
11446
11447             r.argument = o ? o.argument : null;
11448
11449             try { //
11450                 var doc;
11451                 if(Roo.isIE){
11452                     doc = frame.contentWindow.document;
11453                 }else {
11454                     doc = (frame.contentDocument || window.frames[id].document);
11455                 }
11456                 if(doc && doc.body){
11457                     r.responseText = doc.body.innerHTML;
11458                 }
11459                 if(doc && doc.XMLDocument){
11460                     r.responseXML = doc.XMLDocument;
11461                 }else {
11462                     r.responseXML = doc;
11463                 }
11464             }
11465             catch(e) {
11466                 // ignore
11467             }
11468
11469             Roo.EventManager.removeListener(frame, 'load', cb, this);
11470
11471             this.fireEvent("requestcomplete", this, r, o);
11472             Roo.callback(o.success, o.scope, [r, o]);
11473             Roo.callback(o.callback, o.scope, [o, true, r]);
11474
11475             setTimeout(function(){document.body.removeChild(frame);}, 100);
11476         }
11477
11478         Roo.EventManager.on(frame, 'load', cb, this);
11479         form.submit();
11480
11481         if(hiddens){ // remove dynamic params
11482             for(var i = 0, len = hiddens.length; i < len; i++){
11483                 form.removeChild(hiddens[i]);
11484             }
11485         }
11486     }
11487 });
11488
11489 /**
11490  * @class Roo.Ajax
11491  * @extends Roo.data.Connection
11492  * Global Ajax request class.
11493  *
11494  * @singleton
11495  */
11496 Roo.Ajax = new Roo.data.Connection({
11497     // fix up the docs
11498    /**
11499      * @cfg {String} url @hide
11500      */
11501     /**
11502      * @cfg {Object} extraParams @hide
11503      */
11504     /**
11505      * @cfg {Object} defaultHeaders @hide
11506      */
11507     /**
11508      * @cfg {String} method (Optional) @hide
11509      */
11510     /**
11511      * @cfg {Number} timeout (Optional) @hide
11512      */
11513     /**
11514      * @cfg {Boolean} autoAbort (Optional) @hide
11515      */
11516
11517     /**
11518      * @cfg {Boolean} disableCaching (Optional) @hide
11519      */
11520
11521     /**
11522      * @property  disableCaching
11523      * True to add a unique cache-buster param to GET requests. (defaults to true)
11524      * @type Boolean
11525      */
11526     /**
11527      * @property  url
11528      * The default URL to be used for requests to the server. (defaults to undefined)
11529      * @type String
11530      */
11531     /**
11532      * @property  extraParams
11533      * An object containing properties which are used as
11534      * extra parameters to each request made by this object. (defaults to undefined)
11535      * @type Object
11536      */
11537     /**
11538      * @property  defaultHeaders
11539      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11540      * @type Object
11541      */
11542     /**
11543      * @property  method
11544      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11545      * @type String
11546      */
11547     /**
11548      * @property  timeout
11549      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11550      * @type Number
11551      */
11552
11553     /**
11554      * @property  autoAbort
11555      * Whether a new request should abort any pending requests. (defaults to false)
11556      * @type Boolean
11557      */
11558     autoAbort : false,
11559
11560     /**
11561      * Serialize the passed form into a url encoded string
11562      * @param {String/HTMLElement} form
11563      * @return {String}
11564      */
11565     serializeForm : function(form){
11566         return Roo.lib.Ajax.serializeForm(form);
11567     }
11568 });/*
11569  * Based on:
11570  * Ext JS Library 1.1.1
11571  * Copyright(c) 2006-2007, Ext JS, LLC.
11572  *
11573  * Originally Released Under LGPL - original licence link has changed is not relivant.
11574  *
11575  * Fork - LGPL
11576  * <script type="text/javascript">
11577  */
11578  
11579 /**
11580  * Global Ajax request class.
11581  * 
11582  * @class Roo.Ajax
11583  * @extends Roo.data.Connection
11584  * @static
11585  * 
11586  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11587  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11588  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11589  * @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)
11590  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11591  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11592  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11593  */
11594 Roo.Ajax = new Roo.data.Connection({
11595     // fix up the docs
11596     /**
11597      * @scope Roo.Ajax
11598      * @type {Boolear} 
11599      */
11600     autoAbort : false,
11601
11602     /**
11603      * Serialize the passed form into a url encoded string
11604      * @scope Roo.Ajax
11605      * @param {String/HTMLElement} form
11606      * @return {String}
11607      */
11608     serializeForm : function(form){
11609         return Roo.lib.Ajax.serializeForm(form);
11610     }
11611 });/*
11612  * Based on:
11613  * Ext JS Library 1.1.1
11614  * Copyright(c) 2006-2007, Ext JS, LLC.
11615  *
11616  * Originally Released Under LGPL - original licence link has changed is not relivant.
11617  *
11618  * Fork - LGPL
11619  * <script type="text/javascript">
11620  */
11621
11622  
11623 /**
11624  * @class Roo.UpdateManager
11625  * @extends Roo.util.Observable
11626  * Provides AJAX-style update for Element object.<br><br>
11627  * Usage:<br>
11628  * <pre><code>
11629  * // Get it from a Roo.Element object
11630  * var el = Roo.get("foo");
11631  * var mgr = el.getUpdateManager();
11632  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11633  * ...
11634  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11635  * <br>
11636  * // or directly (returns the same UpdateManager instance)
11637  * var mgr = new Roo.UpdateManager("myElementId");
11638  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11639  * mgr.on("update", myFcnNeedsToKnow);
11640  * <br>
11641    // short handed call directly from the element object
11642    Roo.get("foo").load({
11643         url: "bar.php",
11644         scripts:true,
11645         params: "for=bar",
11646         text: "Loading Foo..."
11647    });
11648  * </code></pre>
11649  * @constructor
11650  * Create new UpdateManager directly.
11651  * @param {String/HTMLElement/Roo.Element} el The element to update
11652  * @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).
11653  */
11654 Roo.UpdateManager = function(el, forceNew){
11655     el = Roo.get(el);
11656     if(!forceNew && el.updateManager){
11657         return el.updateManager;
11658     }
11659     /**
11660      * The Element object
11661      * @type Roo.Element
11662      */
11663     this.el = el;
11664     /**
11665      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11666      * @type String
11667      */
11668     this.defaultUrl = null;
11669
11670     this.addEvents({
11671         /**
11672          * @event beforeupdate
11673          * Fired before an update is made, return false from your handler and the update is cancelled.
11674          * @param {Roo.Element} el
11675          * @param {String/Object/Function} url
11676          * @param {String/Object} params
11677          */
11678         "beforeupdate": true,
11679         /**
11680          * @event update
11681          * Fired after successful update is made.
11682          * @param {Roo.Element} el
11683          * @param {Object} oResponseObject The response Object
11684          */
11685         "update": true,
11686         /**
11687          * @event failure
11688          * Fired on update failure.
11689          * @param {Roo.Element} el
11690          * @param {Object} oResponseObject The response Object
11691          */
11692         "failure": true
11693     });
11694     var d = Roo.UpdateManager.defaults;
11695     /**
11696      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11697      * @type String
11698      */
11699     this.sslBlankUrl = d.sslBlankUrl;
11700     /**
11701      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11702      * @type Boolean
11703      */
11704     this.disableCaching = d.disableCaching;
11705     /**
11706      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11707      * @type String
11708      */
11709     this.indicatorText = d.indicatorText;
11710     /**
11711      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11712      * @type String
11713      */
11714     this.showLoadIndicator = d.showLoadIndicator;
11715     /**
11716      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11717      * @type Number
11718      */
11719     this.timeout = d.timeout;
11720
11721     /**
11722      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11723      * @type Boolean
11724      */
11725     this.loadScripts = d.loadScripts;
11726
11727     /**
11728      * Transaction object of current executing transaction
11729      */
11730     this.transaction = null;
11731
11732     /**
11733      * @private
11734      */
11735     this.autoRefreshProcId = null;
11736     /**
11737      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11738      * @type Function
11739      */
11740     this.refreshDelegate = this.refresh.createDelegate(this);
11741     /**
11742      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11743      * @type Function
11744      */
11745     this.updateDelegate = this.update.createDelegate(this);
11746     /**
11747      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11748      * @type Function
11749      */
11750     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11751     /**
11752      * @private
11753      */
11754     this.successDelegate = this.processSuccess.createDelegate(this);
11755     /**
11756      * @private
11757      */
11758     this.failureDelegate = this.processFailure.createDelegate(this);
11759
11760     if(!this.renderer){
11761      /**
11762       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11763       */
11764     this.renderer = new Roo.UpdateManager.BasicRenderer();
11765     }
11766     
11767     Roo.UpdateManager.superclass.constructor.call(this);
11768 };
11769
11770 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11771     /**
11772      * Get the Element this UpdateManager is bound to
11773      * @return {Roo.Element} The element
11774      */
11775     getEl : function(){
11776         return this.el;
11777     },
11778     /**
11779      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11780      * @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:
11781 <pre><code>
11782 um.update({<br/>
11783     url: "your-url.php",<br/>
11784     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11785     callback: yourFunction,<br/>
11786     scope: yourObject, //(optional scope)  <br/>
11787     discardUrl: false, <br/>
11788     nocache: false,<br/>
11789     text: "Loading...",<br/>
11790     timeout: 30,<br/>
11791     scripts: false<br/>
11792 });
11793 </code></pre>
11794      * The only required property is url. The optional properties nocache, text and scripts
11795      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11796      * @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}
11797      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11798      * @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.
11799      */
11800     update : function(url, params, callback, discardUrl){
11801         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11802             var method = this.method, cfg;
11803             if(typeof url == "object"){ // must be config object
11804                 cfg = url;
11805                 url = cfg.url;
11806                 params = params || cfg.params;
11807                 callback = callback || cfg.callback;
11808                 discardUrl = discardUrl || cfg.discardUrl;
11809                 if(callback && cfg.scope){
11810                     callback = callback.createDelegate(cfg.scope);
11811                 }
11812                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11813                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11814                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11815                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11816                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11817             }
11818             this.showLoading();
11819             if(!discardUrl){
11820                 this.defaultUrl = url;
11821             }
11822             if(typeof url == "function"){
11823                 url = url.call(this);
11824             }
11825
11826             method = method || (params ? "POST" : "GET");
11827             if(method == "GET"){
11828                 url = this.prepareUrl(url);
11829             }
11830
11831             var o = Roo.apply(cfg ||{}, {
11832                 url : url,
11833                 params: params,
11834                 success: this.successDelegate,
11835                 failure: this.failureDelegate,
11836                 callback: undefined,
11837                 timeout: (this.timeout*1000),
11838                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11839             });
11840
11841             this.transaction = Roo.Ajax.request(o);
11842         }
11843     },
11844
11845     /**
11846      * 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.
11847      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11848      * @param {String/HTMLElement} form The form Id or form element
11849      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11850      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11851      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11852      */
11853     formUpdate : function(form, url, reset, callback){
11854         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11855             if(typeof url == "function"){
11856                 url = url.call(this);
11857             }
11858             form = Roo.getDom(form);
11859             this.transaction = Roo.Ajax.request({
11860                 form: form,
11861                 url:url,
11862                 success: this.successDelegate,
11863                 failure: this.failureDelegate,
11864                 timeout: (this.timeout*1000),
11865                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11866             });
11867             this.showLoading.defer(1, this);
11868         }
11869     },
11870
11871     /**
11872      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11873      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11874      */
11875     refresh : function(callback){
11876         if(this.defaultUrl == null){
11877             return;
11878         }
11879         this.update(this.defaultUrl, null, callback, true);
11880     },
11881
11882     /**
11883      * Set this element to auto refresh.
11884      * @param {Number} interval How often to update (in seconds).
11885      * @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)
11886      * @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}
11887      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11888      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11889      */
11890     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11891         if(refreshNow){
11892             this.update(url || this.defaultUrl, params, callback, true);
11893         }
11894         if(this.autoRefreshProcId){
11895             clearInterval(this.autoRefreshProcId);
11896         }
11897         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11898     },
11899
11900     /**
11901      * Stop auto refresh on this element.
11902      */
11903      stopAutoRefresh : function(){
11904         if(this.autoRefreshProcId){
11905             clearInterval(this.autoRefreshProcId);
11906             delete this.autoRefreshProcId;
11907         }
11908     },
11909
11910     isAutoRefreshing : function(){
11911        return this.autoRefreshProcId ? true : false;
11912     },
11913     /**
11914      * Called to update the element to "Loading" state. Override to perform custom action.
11915      */
11916     showLoading : function(){
11917         if(this.showLoadIndicator){
11918             this.el.update(this.indicatorText);
11919         }
11920     },
11921
11922     /**
11923      * Adds unique parameter to query string if disableCaching = true
11924      * @private
11925      */
11926     prepareUrl : function(url){
11927         if(this.disableCaching){
11928             var append = "_dc=" + (new Date().getTime());
11929             if(url.indexOf("?") !== -1){
11930                 url += "&" + append;
11931             }else{
11932                 url += "?" + append;
11933             }
11934         }
11935         return url;
11936     },
11937
11938     /**
11939      * @private
11940      */
11941     processSuccess : function(response){
11942         this.transaction = null;
11943         if(response.argument.form && response.argument.reset){
11944             try{ // put in try/catch since some older FF releases had problems with this
11945                 response.argument.form.reset();
11946             }catch(e){}
11947         }
11948         if(this.loadScripts){
11949             this.renderer.render(this.el, response, this,
11950                 this.updateComplete.createDelegate(this, [response]));
11951         }else{
11952             this.renderer.render(this.el, response, this);
11953             this.updateComplete(response);
11954         }
11955     },
11956
11957     updateComplete : function(response){
11958         this.fireEvent("update", this.el, response);
11959         if(typeof response.argument.callback == "function"){
11960             response.argument.callback(this.el, true, response);
11961         }
11962     },
11963
11964     /**
11965      * @private
11966      */
11967     processFailure : function(response){
11968         this.transaction = null;
11969         this.fireEvent("failure", this.el, response);
11970         if(typeof response.argument.callback == "function"){
11971             response.argument.callback(this.el, false, response);
11972         }
11973     },
11974
11975     /**
11976      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11977      * @param {Object} renderer The object implementing the render() method
11978      */
11979     setRenderer : function(renderer){
11980         this.renderer = renderer;
11981     },
11982
11983     getRenderer : function(){
11984        return this.renderer;
11985     },
11986
11987     /**
11988      * Set the defaultUrl used for updates
11989      * @param {String/Function} defaultUrl The url or a function to call to get the url
11990      */
11991     setDefaultUrl : function(defaultUrl){
11992         this.defaultUrl = defaultUrl;
11993     },
11994
11995     /**
11996      * Aborts the executing transaction
11997      */
11998     abort : function(){
11999         if(this.transaction){
12000             Roo.Ajax.abort(this.transaction);
12001         }
12002     },
12003
12004     /**
12005      * Returns true if an update is in progress
12006      * @return {Boolean}
12007      */
12008     isUpdating : function(){
12009         if(this.transaction){
12010             return Roo.Ajax.isLoading(this.transaction);
12011         }
12012         return false;
12013     }
12014 });
12015
12016 /**
12017  * @class Roo.UpdateManager.defaults
12018  * @static (not really - but it helps the doc tool)
12019  * The defaults collection enables customizing the default properties of UpdateManager
12020  */
12021    Roo.UpdateManager.defaults = {
12022        /**
12023          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12024          * @type Number
12025          */
12026          timeout : 30,
12027
12028          /**
12029          * True to process scripts by default (Defaults to false).
12030          * @type Boolean
12031          */
12032         loadScripts : false,
12033
12034         /**
12035         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12036         * @type String
12037         */
12038         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12039         /**
12040          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12041          * @type Boolean
12042          */
12043         disableCaching : false,
12044         /**
12045          * Whether to show indicatorText when loading (Defaults to true).
12046          * @type Boolean
12047          */
12048         showLoadIndicator : true,
12049         /**
12050          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12051          * @type String
12052          */
12053         indicatorText : '<div class="loading-indicator">Loading...</div>'
12054    };
12055
12056 /**
12057  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12058  *Usage:
12059  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12060  * @param {String/HTMLElement/Roo.Element} el The element to update
12061  * @param {String} url The url
12062  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12063  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12064  * @static
12065  * @deprecated
12066  * @member Roo.UpdateManager
12067  */
12068 Roo.UpdateManager.updateElement = function(el, url, params, options){
12069     var um = Roo.get(el, true).getUpdateManager();
12070     Roo.apply(um, options);
12071     um.update(url, params, options ? options.callback : null);
12072 };
12073 // alias for backwards compat
12074 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12075 /**
12076  * @class Roo.UpdateManager.BasicRenderer
12077  * Default Content renderer. Updates the elements innerHTML with the responseText.
12078  */
12079 Roo.UpdateManager.BasicRenderer = function(){};
12080
12081 Roo.UpdateManager.BasicRenderer.prototype = {
12082     /**
12083      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12084      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12085      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12086      * @param {Roo.Element} el The element being rendered
12087      * @param {Object} response The YUI Connect response object
12088      * @param {UpdateManager} updateManager The calling update manager
12089      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12090      */
12091      render : function(el, response, updateManager, callback){
12092         el.update(response.responseText, updateManager.loadScripts, callback);
12093     }
12094 };
12095 /*
12096  * Based on:
12097  * Ext JS Library 1.1.1
12098  * Copyright(c) 2006-2007, Ext JS, LLC.
12099  *
12100  * Originally Released Under LGPL - original licence link has changed is not relivant.
12101  *
12102  * Fork - LGPL
12103  * <script type="text/javascript">
12104  */
12105
12106 /**
12107  * @class Roo.util.DelayedTask
12108  * Provides a convenient method of performing setTimeout where a new
12109  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12110  * You can use this class to buffer
12111  * the keypress events for a certain number of milliseconds, and perform only if they stop
12112  * for that amount of time.
12113  * @constructor The parameters to this constructor serve as defaults and are not required.
12114  * @param {Function} fn (optional) The default function to timeout
12115  * @param {Object} scope (optional) The default scope of that timeout
12116  * @param {Array} args (optional) The default Array of arguments
12117  */
12118 Roo.util.DelayedTask = function(fn, scope, args){
12119     var id = null, d, t;
12120
12121     var call = function(){
12122         var now = new Date().getTime();
12123         if(now - t >= d){
12124             clearInterval(id);
12125             id = null;
12126             fn.apply(scope, args || []);
12127         }
12128     };
12129     /**
12130      * Cancels any pending timeout and queues a new one
12131      * @param {Number} delay The milliseconds to delay
12132      * @param {Function} newFn (optional) Overrides function passed to constructor
12133      * @param {Object} newScope (optional) Overrides scope passed to constructor
12134      * @param {Array} newArgs (optional) Overrides args passed to constructor
12135      */
12136     this.delay = function(delay, newFn, newScope, newArgs){
12137         if(id && delay != d){
12138             this.cancel();
12139         }
12140         d = delay;
12141         t = new Date().getTime();
12142         fn = newFn || fn;
12143         scope = newScope || scope;
12144         args = newArgs || args;
12145         if(!id){
12146             id = setInterval(call, d);
12147         }
12148     };
12149
12150     /**
12151      * Cancel the last queued timeout
12152      */
12153     this.cancel = function(){
12154         if(id){
12155             clearInterval(id);
12156             id = null;
12157         }
12158     };
12159 };/*
12160  * Based on:
12161  * Ext JS Library 1.1.1
12162  * Copyright(c) 2006-2007, Ext JS, LLC.
12163  *
12164  * Originally Released Under LGPL - original licence link has changed is not relivant.
12165  *
12166  * Fork - LGPL
12167  * <script type="text/javascript">
12168  */
12169  
12170  
12171 Roo.util.TaskRunner = function(interval){
12172     interval = interval || 10;
12173     var tasks = [], removeQueue = [];
12174     var id = 0;
12175     var running = false;
12176
12177     var stopThread = function(){
12178         running = false;
12179         clearInterval(id);
12180         id = 0;
12181     };
12182
12183     var startThread = function(){
12184         if(!running){
12185             running = true;
12186             id = setInterval(runTasks, interval);
12187         }
12188     };
12189
12190     var removeTask = function(task){
12191         removeQueue.push(task);
12192         if(task.onStop){
12193             task.onStop();
12194         }
12195     };
12196
12197     var runTasks = function(){
12198         if(removeQueue.length > 0){
12199             for(var i = 0, len = removeQueue.length; i < len; i++){
12200                 tasks.remove(removeQueue[i]);
12201             }
12202             removeQueue = [];
12203             if(tasks.length < 1){
12204                 stopThread();
12205                 return;
12206             }
12207         }
12208         var now = new Date().getTime();
12209         for(var i = 0, len = tasks.length; i < len; ++i){
12210             var t = tasks[i];
12211             var itime = now - t.taskRunTime;
12212             if(t.interval <= itime){
12213                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12214                 t.taskRunTime = now;
12215                 if(rt === false || t.taskRunCount === t.repeat){
12216                     removeTask(t);
12217                     return;
12218                 }
12219             }
12220             if(t.duration && t.duration <= (now - t.taskStartTime)){
12221                 removeTask(t);
12222             }
12223         }
12224     };
12225
12226     /**
12227      * Queues a new task.
12228      * @param {Object} task
12229      */
12230     this.start = function(task){
12231         tasks.push(task);
12232         task.taskStartTime = new Date().getTime();
12233         task.taskRunTime = 0;
12234         task.taskRunCount = 0;
12235         startThread();
12236         return task;
12237     };
12238
12239     this.stop = function(task){
12240         removeTask(task);
12241         return task;
12242     };
12243
12244     this.stopAll = function(){
12245         stopThread();
12246         for(var i = 0, len = tasks.length; i < len; i++){
12247             if(tasks[i].onStop){
12248                 tasks[i].onStop();
12249             }
12250         }
12251         tasks = [];
12252         removeQueue = [];
12253     };
12254 };
12255
12256 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12257  * Based on:
12258  * Ext JS Library 1.1.1
12259  * Copyright(c) 2006-2007, Ext JS, LLC.
12260  *
12261  * Originally Released Under LGPL - original licence link has changed is not relivant.
12262  *
12263  * Fork - LGPL
12264  * <script type="text/javascript">
12265  */
12266
12267  
12268 /**
12269  * @class Roo.util.MixedCollection
12270  * @extends Roo.util.Observable
12271  * A Collection class that maintains both numeric indexes and keys and exposes events.
12272  * @constructor
12273  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12274  * collection (defaults to false)
12275  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12276  * and return the key value for that item.  This is used when available to look up the key on items that
12277  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12278  * equivalent to providing an implementation for the {@link #getKey} method.
12279  */
12280 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12281     this.items = [];
12282     this.map = {};
12283     this.keys = [];
12284     this.length = 0;
12285     this.addEvents({
12286         /**
12287          * @event clear
12288          * Fires when the collection is cleared.
12289          */
12290         "clear" : true,
12291         /**
12292          * @event add
12293          * Fires when an item is added to the collection.
12294          * @param {Number} index The index at which the item was added.
12295          * @param {Object} o The item added.
12296          * @param {String} key The key associated with the added item.
12297          */
12298         "add" : true,
12299         /**
12300          * @event replace
12301          * Fires when an item is replaced in the collection.
12302          * @param {String} key he key associated with the new added.
12303          * @param {Object} old The item being replaced.
12304          * @param {Object} new The new item.
12305          */
12306         "replace" : true,
12307         /**
12308          * @event remove
12309          * Fires when an item is removed from the collection.
12310          * @param {Object} o The item being removed.
12311          * @param {String} key (optional) The key associated with the removed item.
12312          */
12313         "remove" : true,
12314         "sort" : true
12315     });
12316     this.allowFunctions = allowFunctions === true;
12317     if(keyFn){
12318         this.getKey = keyFn;
12319     }
12320     Roo.util.MixedCollection.superclass.constructor.call(this);
12321 };
12322
12323 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12324     allowFunctions : false,
12325     
12326 /**
12327  * Adds an item to the collection.
12328  * @param {String} key The key to associate with the item
12329  * @param {Object} o The item to add.
12330  * @return {Object} The item added.
12331  */
12332     add : function(key, o){
12333         if(arguments.length == 1){
12334             o = arguments[0];
12335             key = this.getKey(o);
12336         }
12337         if(typeof key == "undefined" || key === null){
12338             this.length++;
12339             this.items.push(o);
12340             this.keys.push(null);
12341         }else{
12342             var old = this.map[key];
12343             if(old){
12344                 return this.replace(key, o);
12345             }
12346             this.length++;
12347             this.items.push(o);
12348             this.map[key] = o;
12349             this.keys.push(key);
12350         }
12351         this.fireEvent("add", this.length-1, o, key);
12352         return o;
12353     },
12354        
12355 /**
12356   * MixedCollection has a generic way to fetch keys if you implement getKey.
12357 <pre><code>
12358 // normal way
12359 var mc = new Roo.util.MixedCollection();
12360 mc.add(someEl.dom.id, someEl);
12361 mc.add(otherEl.dom.id, otherEl);
12362 //and so on
12363
12364 // using getKey
12365 var mc = new Roo.util.MixedCollection();
12366 mc.getKey = function(el){
12367    return el.dom.id;
12368 };
12369 mc.add(someEl);
12370 mc.add(otherEl);
12371
12372 // or via the constructor
12373 var mc = new Roo.util.MixedCollection(false, function(el){
12374    return el.dom.id;
12375 });
12376 mc.add(someEl);
12377 mc.add(otherEl);
12378 </code></pre>
12379  * @param o {Object} The item for which to find the key.
12380  * @return {Object} The key for the passed item.
12381  */
12382     getKey : function(o){
12383          return o.id; 
12384     },
12385    
12386 /**
12387  * Replaces an item in the collection.
12388  * @param {String} key The key associated with the item to replace, or the item to replace.
12389  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12390  * @return {Object}  The new item.
12391  */
12392     replace : function(key, o){
12393         if(arguments.length == 1){
12394             o = arguments[0];
12395             key = this.getKey(o);
12396         }
12397         var old = this.item(key);
12398         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12399              return this.add(key, o);
12400         }
12401         var index = this.indexOfKey(key);
12402         this.items[index] = o;
12403         this.map[key] = o;
12404         this.fireEvent("replace", key, old, o);
12405         return o;
12406     },
12407    
12408 /**
12409  * Adds all elements of an Array or an Object to the collection.
12410  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12411  * an Array of values, each of which are added to the collection.
12412  */
12413     addAll : function(objs){
12414         if(arguments.length > 1 || objs instanceof Array){
12415             var args = arguments.length > 1 ? arguments : objs;
12416             for(var i = 0, len = args.length; i < len; i++){
12417                 this.add(args[i]);
12418             }
12419         }else{
12420             for(var key in objs){
12421                 if(this.allowFunctions || typeof objs[key] != "function"){
12422                     this.add(key, objs[key]);
12423                 }
12424             }
12425         }
12426     },
12427    
12428 /**
12429  * Executes the specified function once for every item in the collection, passing each
12430  * item as the first and only parameter. returning false from the function will stop the iteration.
12431  * @param {Function} fn The function to execute for each item.
12432  * @param {Object} scope (optional) The scope in which to execute the function.
12433  */
12434     each : function(fn, scope){
12435         var items = [].concat(this.items); // each safe for removal
12436         for(var i = 0, len = items.length; i < len; i++){
12437             if(fn.call(scope || items[i], items[i], i, len) === false){
12438                 break;
12439             }
12440         }
12441     },
12442    
12443 /**
12444  * Executes the specified function once for every key in the collection, passing each
12445  * key, and its associated item as the first two parameters.
12446  * @param {Function} fn The function to execute for each item.
12447  * @param {Object} scope (optional) The scope in which to execute the function.
12448  */
12449     eachKey : function(fn, scope){
12450         for(var i = 0, len = this.keys.length; i < len; i++){
12451             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12452         }
12453     },
12454    
12455 /**
12456  * Returns the first item in the collection which elicits a true return value from the
12457  * passed selection function.
12458  * @param {Function} fn The selection function to execute for each item.
12459  * @param {Object} scope (optional) The scope in which to execute the function.
12460  * @return {Object} The first item in the collection which returned true from the selection function.
12461  */
12462     find : function(fn, scope){
12463         for(var i = 0, len = this.items.length; i < len; i++){
12464             if(fn.call(scope || window, this.items[i], this.keys[i])){
12465                 return this.items[i];
12466             }
12467         }
12468         return null;
12469     },
12470    
12471 /**
12472  * Inserts an item at the specified index in the collection.
12473  * @param {Number} index The index to insert the item at.
12474  * @param {String} key The key to associate with the new item, or the item itself.
12475  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12476  * @return {Object} The item inserted.
12477  */
12478     insert : function(index, key, o){
12479         if(arguments.length == 2){
12480             o = arguments[1];
12481             key = this.getKey(o);
12482         }
12483         if(index >= this.length){
12484             return this.add(key, o);
12485         }
12486         this.length++;
12487         this.items.splice(index, 0, o);
12488         if(typeof key != "undefined" && key != null){
12489             this.map[key] = o;
12490         }
12491         this.keys.splice(index, 0, key);
12492         this.fireEvent("add", index, o, key);
12493         return o;
12494     },
12495    
12496 /**
12497  * Removed an item from the collection.
12498  * @param {Object} o The item to remove.
12499  * @return {Object} The item removed.
12500  */
12501     remove : function(o){
12502         return this.removeAt(this.indexOf(o));
12503     },
12504    
12505 /**
12506  * Remove an item from a specified index in the collection.
12507  * @param {Number} index The index within the collection of the item to remove.
12508  */
12509     removeAt : function(index){
12510         if(index < this.length && index >= 0){
12511             this.length--;
12512             var o = this.items[index];
12513             this.items.splice(index, 1);
12514             var key = this.keys[index];
12515             if(typeof key != "undefined"){
12516                 delete this.map[key];
12517             }
12518             this.keys.splice(index, 1);
12519             this.fireEvent("remove", o, key);
12520         }
12521     },
12522    
12523 /**
12524  * Removed an item associated with the passed key fom the collection.
12525  * @param {String} key The key of the item to remove.
12526  */
12527     removeKey : function(key){
12528         return this.removeAt(this.indexOfKey(key));
12529     },
12530    
12531 /**
12532  * Returns the number of items in the collection.
12533  * @return {Number} the number of items in the collection.
12534  */
12535     getCount : function(){
12536         return this.length; 
12537     },
12538    
12539 /**
12540  * Returns index within the collection of the passed Object.
12541  * @param {Object} o The item to find the index of.
12542  * @return {Number} index of the item.
12543  */
12544     indexOf : function(o){
12545         if(!this.items.indexOf){
12546             for(var i = 0, len = this.items.length; i < len; i++){
12547                 if(this.items[i] == o) return i;
12548             }
12549             return -1;
12550         }else{
12551             return this.items.indexOf(o);
12552         }
12553     },
12554    
12555 /**
12556  * Returns index within the collection of the passed key.
12557  * @param {String} key The key to find the index of.
12558  * @return {Number} index of the key.
12559  */
12560     indexOfKey : function(key){
12561         if(!this.keys.indexOf){
12562             for(var i = 0, len = this.keys.length; i < len; i++){
12563                 if(this.keys[i] == key) return i;
12564             }
12565             return -1;
12566         }else{
12567             return this.keys.indexOf(key);
12568         }
12569     },
12570    
12571 /**
12572  * Returns the item associated with the passed key OR index. Key has priority over index.
12573  * @param {String/Number} key The key or index of the item.
12574  * @return {Object} The item associated with the passed key.
12575  */
12576     item : function(key){
12577         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12578         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12579     },
12580     
12581 /**
12582  * Returns the item at the specified index.
12583  * @param {Number} index The index of the item.
12584  * @return {Object}
12585  */
12586     itemAt : function(index){
12587         return this.items[index];
12588     },
12589     
12590 /**
12591  * Returns the item associated with the passed key.
12592  * @param {String/Number} key The key of the item.
12593  * @return {Object} The item associated with the passed key.
12594  */
12595     key : function(key){
12596         return this.map[key];
12597     },
12598    
12599 /**
12600  * Returns true if the collection contains the passed Object as an item.
12601  * @param {Object} o  The Object to look for in the collection.
12602  * @return {Boolean} True if the collection contains the Object as an item.
12603  */
12604     contains : function(o){
12605         return this.indexOf(o) != -1;
12606     },
12607    
12608 /**
12609  * Returns true if the collection contains the passed Object as a key.
12610  * @param {String} key The key to look for in the collection.
12611  * @return {Boolean} True if the collection contains the Object as a key.
12612  */
12613     containsKey : function(key){
12614         return typeof this.map[key] != "undefined";
12615     },
12616    
12617 /**
12618  * Removes all items from the collection.
12619  */
12620     clear : function(){
12621         this.length = 0;
12622         this.items = [];
12623         this.keys = [];
12624         this.map = {};
12625         this.fireEvent("clear");
12626     },
12627    
12628 /**
12629  * Returns the first item in the collection.
12630  * @return {Object} the first item in the collection..
12631  */
12632     first : function(){
12633         return this.items[0]; 
12634     },
12635    
12636 /**
12637  * Returns the last item in the collection.
12638  * @return {Object} the last item in the collection..
12639  */
12640     last : function(){
12641         return this.items[this.length-1];   
12642     },
12643     
12644     _sort : function(property, dir, fn){
12645         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12646         fn = fn || function(a, b){
12647             return a-b;
12648         };
12649         var c = [], k = this.keys, items = this.items;
12650         for(var i = 0, len = items.length; i < len; i++){
12651             c[c.length] = {key: k[i], value: items[i], index: i};
12652         }
12653         c.sort(function(a, b){
12654             var v = fn(a[property], b[property]) * dsc;
12655             if(v == 0){
12656                 v = (a.index < b.index ? -1 : 1);
12657             }
12658             return v;
12659         });
12660         for(var i = 0, len = c.length; i < len; i++){
12661             items[i] = c[i].value;
12662             k[i] = c[i].key;
12663         }
12664         this.fireEvent("sort", this);
12665     },
12666     
12667     /**
12668      * Sorts this collection with the passed comparison function
12669      * @param {String} direction (optional) "ASC" or "DESC"
12670      * @param {Function} fn (optional) comparison function
12671      */
12672     sort : function(dir, fn){
12673         this._sort("value", dir, fn);
12674     },
12675     
12676     /**
12677      * Sorts this collection by keys
12678      * @param {String} direction (optional) "ASC" or "DESC"
12679      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12680      */
12681     keySort : function(dir, fn){
12682         this._sort("key", dir, fn || function(a, b){
12683             return String(a).toUpperCase()-String(b).toUpperCase();
12684         });
12685     },
12686     
12687     /**
12688      * Returns a range of items in this collection
12689      * @param {Number} startIndex (optional) defaults to 0
12690      * @param {Number} endIndex (optional) default to the last item
12691      * @return {Array} An array of items
12692      */
12693     getRange : function(start, end){
12694         var items = this.items;
12695         if(items.length < 1){
12696             return [];
12697         }
12698         start = start || 0;
12699         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12700         var r = [];
12701         if(start <= end){
12702             for(var i = start; i <= end; i++) {
12703                     r[r.length] = items[i];
12704             }
12705         }else{
12706             for(var i = start; i >= end; i--) {
12707                     r[r.length] = items[i];
12708             }
12709         }
12710         return r;
12711     },
12712         
12713     /**
12714      * Filter the <i>objects</i> in this collection by a specific property. 
12715      * Returns a new collection that has been filtered.
12716      * @param {String} property A property on your objects
12717      * @param {String/RegExp} value Either string that the property values 
12718      * should start with or a RegExp to test against the property
12719      * @return {MixedCollection} The new filtered collection
12720      */
12721     filter : function(property, value){
12722         if(!value.exec){ // not a regex
12723             value = String(value);
12724             if(value.length == 0){
12725                 return this.clone();
12726             }
12727             value = new RegExp("^" + Roo.escapeRe(value), "i");
12728         }
12729         return this.filterBy(function(o){
12730             return o && value.test(o[property]);
12731         });
12732         },
12733     
12734     /**
12735      * Filter by a function. * Returns a new collection that has been filtered.
12736      * The passed function will be called with each 
12737      * object in the collection. If the function returns true, the value is included 
12738      * otherwise it is filtered.
12739      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12740      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12741      * @return {MixedCollection} The new filtered collection
12742      */
12743     filterBy : function(fn, scope){
12744         var r = new Roo.util.MixedCollection();
12745         r.getKey = this.getKey;
12746         var k = this.keys, it = this.items;
12747         for(var i = 0, len = it.length; i < len; i++){
12748             if(fn.call(scope||this, it[i], k[i])){
12749                                 r.add(k[i], it[i]);
12750                         }
12751         }
12752         return r;
12753     },
12754     
12755     /**
12756      * Creates a duplicate of this collection
12757      * @return {MixedCollection}
12758      */
12759     clone : function(){
12760         var r = new Roo.util.MixedCollection();
12761         var k = this.keys, it = this.items;
12762         for(var i = 0, len = it.length; i < len; i++){
12763             r.add(k[i], it[i]);
12764         }
12765         r.getKey = this.getKey;
12766         return r;
12767     }
12768 });
12769 /**
12770  * Returns the item associated with the passed key or index.
12771  * @method
12772  * @param {String/Number} key The key or index of the item.
12773  * @return {Object} The item associated with the passed key.
12774  */
12775 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12776  * Based on:
12777  * Ext JS Library 1.1.1
12778  * Copyright(c) 2006-2007, Ext JS, LLC.
12779  *
12780  * Originally Released Under LGPL - original licence link has changed is not relivant.
12781  *
12782  * Fork - LGPL
12783  * <script type="text/javascript">
12784  */
12785 /**
12786  * @class Roo.util.JSON
12787  * Modified version of Douglas Crockford"s json.js that doesn"t
12788  * mess with the Object prototype 
12789  * http://www.json.org/js.html
12790  * @singleton
12791  */
12792 Roo.util.JSON = new (function(){
12793     var useHasOwn = {}.hasOwnProperty ? true : false;
12794     
12795     // crashes Safari in some instances
12796     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12797     
12798     var pad = function(n) {
12799         return n < 10 ? "0" + n : n;
12800     };
12801     
12802     var m = {
12803         "\b": '\\b',
12804         "\t": '\\t',
12805         "\n": '\\n',
12806         "\f": '\\f',
12807         "\r": '\\r',
12808         '"' : '\\"',
12809         "\\": '\\\\'
12810     };
12811
12812     var encodeString = function(s){
12813         if (/["\\\x00-\x1f]/.test(s)) {
12814             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12815                 var c = m[b];
12816                 if(c){
12817                     return c;
12818                 }
12819                 c = b.charCodeAt();
12820                 return "\\u00" +
12821                     Math.floor(c / 16).toString(16) +
12822                     (c % 16).toString(16);
12823             }) + '"';
12824         }
12825         return '"' + s + '"';
12826     };
12827     
12828     var encodeArray = function(o){
12829         var a = ["["], b, i, l = o.length, v;
12830             for (i = 0; i < l; i += 1) {
12831                 v = o[i];
12832                 switch (typeof v) {
12833                     case "undefined":
12834                     case "function":
12835                     case "unknown":
12836                         break;
12837                     default:
12838                         if (b) {
12839                             a.push(',');
12840                         }
12841                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12842                         b = true;
12843                 }
12844             }
12845             a.push("]");
12846             return a.join("");
12847     };
12848     
12849     var encodeDate = function(o){
12850         return '"' + o.getFullYear() + "-" +
12851                 pad(o.getMonth() + 1) + "-" +
12852                 pad(o.getDate()) + "T" +
12853                 pad(o.getHours()) + ":" +
12854                 pad(o.getMinutes()) + ":" +
12855                 pad(o.getSeconds()) + '"';
12856     };
12857     
12858     /**
12859      * Encodes an Object, Array or other value
12860      * @param {Mixed} o The variable to encode
12861      * @return {String} The JSON string
12862      */
12863     this.encode = function(o)
12864     {
12865         // should this be extended to fully wrap stringify..
12866         
12867         if(typeof o == "undefined" || o === null){
12868             return "null";
12869         }else if(o instanceof Array){
12870             return encodeArray(o);
12871         }else if(o instanceof Date){
12872             return encodeDate(o);
12873         }else if(typeof o == "string"){
12874             return encodeString(o);
12875         }else if(typeof o == "number"){
12876             return isFinite(o) ? String(o) : "null";
12877         }else if(typeof o == "boolean"){
12878             return String(o);
12879         }else {
12880             var a = ["{"], b, i, v;
12881             for (i in o) {
12882                 if(!useHasOwn || o.hasOwnProperty(i)) {
12883                     v = o[i];
12884                     switch (typeof v) {
12885                     case "undefined":
12886                     case "function":
12887                     case "unknown":
12888                         break;
12889                     default:
12890                         if(b){
12891                             a.push(',');
12892                         }
12893                         a.push(this.encode(i), ":",
12894                                 v === null ? "null" : this.encode(v));
12895                         b = true;
12896                     }
12897                 }
12898             }
12899             a.push("}");
12900             return a.join("");
12901         }
12902     };
12903     
12904     /**
12905      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12906      * @param {String} json The JSON string
12907      * @return {Object} The resulting object
12908      */
12909     this.decode = function(json){
12910         
12911         return  /** eval:var:json */ eval("(" + json + ')');
12912     };
12913 })();
12914 /** 
12915  * Shorthand for {@link Roo.util.JSON#encode}
12916  * @member Roo encode 
12917  * @method */
12918 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12919 /** 
12920  * Shorthand for {@link Roo.util.JSON#decode}
12921  * @member Roo decode 
12922  * @method */
12923 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12924 /*
12925  * Based on:
12926  * Ext JS Library 1.1.1
12927  * Copyright(c) 2006-2007, Ext JS, LLC.
12928  *
12929  * Originally Released Under LGPL - original licence link has changed is not relivant.
12930  *
12931  * Fork - LGPL
12932  * <script type="text/javascript">
12933  */
12934  
12935 /**
12936  * @class Roo.util.Format
12937  * Reusable data formatting functions
12938  * @singleton
12939  */
12940 Roo.util.Format = function(){
12941     var trimRe = /^\s+|\s+$/g;
12942     return {
12943         /**
12944          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12945          * @param {String} value The string to truncate
12946          * @param {Number} length The maximum length to allow before truncating
12947          * @return {String} The converted text
12948          */
12949         ellipsis : function(value, len){
12950             if(value && value.length > len){
12951                 return value.substr(0, len-3)+"...";
12952             }
12953             return value;
12954         },
12955
12956         /**
12957          * Checks a reference and converts it to empty string if it is undefined
12958          * @param {Mixed} value Reference to check
12959          * @return {Mixed} Empty string if converted, otherwise the original value
12960          */
12961         undef : function(value){
12962             return typeof value != "undefined" ? value : "";
12963         },
12964
12965         /**
12966          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12967          * @param {String} value The string to encode
12968          * @return {String} The encoded text
12969          */
12970         htmlEncode : function(value){
12971             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12972         },
12973
12974         /**
12975          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12976          * @param {String} value The string to decode
12977          * @return {String} The decoded text
12978          */
12979         htmlDecode : function(value){
12980             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12981         },
12982
12983         /**
12984          * Trims any whitespace from either side of a string
12985          * @param {String} value The text to trim
12986          * @return {String} The trimmed text
12987          */
12988         trim : function(value){
12989             return String(value).replace(trimRe, "");
12990         },
12991
12992         /**
12993          * Returns a substring from within an original string
12994          * @param {String} value The original text
12995          * @param {Number} start The start index of the substring
12996          * @param {Number} length The length of the substring
12997          * @return {String} The substring
12998          */
12999         substr : function(value, start, length){
13000             return String(value).substr(start, length);
13001         },
13002
13003         /**
13004          * Converts a string to all lower case letters
13005          * @param {String} value The text to convert
13006          * @return {String} The converted text
13007          */
13008         lowercase : function(value){
13009             return String(value).toLowerCase();
13010         },
13011
13012         /**
13013          * Converts a string to all upper case letters
13014          * @param {String} value The text to convert
13015          * @return {String} The converted text
13016          */
13017         uppercase : function(value){
13018             return String(value).toUpperCase();
13019         },
13020
13021         /**
13022          * Converts the first character only of a string to upper case
13023          * @param {String} value The text to convert
13024          * @return {String} The converted text
13025          */
13026         capitalize : function(value){
13027             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13028         },
13029
13030         // private
13031         call : function(value, fn){
13032             if(arguments.length > 2){
13033                 var args = Array.prototype.slice.call(arguments, 2);
13034                 args.unshift(value);
13035                  
13036                 return /** eval:var:value */  eval(fn).apply(window, args);
13037             }else{
13038                 /** eval:var:value */
13039                 return /** eval:var:value */ eval(fn).call(window, value);
13040             }
13041         },
13042
13043        
13044         /**
13045          * safer version of Math.toFixed..??/
13046          * @param {Number/String} value The numeric value to format
13047          * @param {Number/String} value Decimal places 
13048          * @return {String} The formatted currency string
13049          */
13050         toFixed : function(v, n)
13051         {
13052             // why not use to fixed - precision is buggered???
13053             if (!n) {
13054                 return Math.round(v-0);
13055             }
13056             var fact = Math.pow(10,n+1);
13057             v = (Math.round((v-0)*fact))/fact;
13058             var z = (''+fact).substring(2);
13059             if (v == Math.floor(v)) {
13060                 return Math.floor(v) + '.' + z;
13061             }
13062             
13063             // now just padd decimals..
13064             var ps = String(v).split('.');
13065             var fd = (ps[1] + z);
13066             var r = fd.substring(0,n); 
13067             var rm = fd.substring(n); 
13068             if (rm < 5) {
13069                 return ps[0] + '.' + r;
13070             }
13071             r*=1; // turn it into a number;
13072             r++;
13073             if (String(r).length != n) {
13074                 ps[0]*=1;
13075                 ps[0]++;
13076                 r = String(r).substring(1); // chop the end off.
13077             }
13078             
13079             return ps[0] + '.' + r;
13080              
13081         },
13082         
13083         /**
13084          * Format a number as US currency
13085          * @param {Number/String} value The numeric value to format
13086          * @return {String} The formatted currency string
13087          */
13088         usMoney : function(v){
13089             v = (Math.round((v-0)*100))/100;
13090             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13091             v = String(v);
13092             var ps = v.split('.');
13093             var whole = ps[0];
13094             var sub = ps[1] ? '.'+ ps[1] : '.00';
13095             var r = /(\d+)(\d{3})/;
13096             while (r.test(whole)) {
13097                 whole = whole.replace(r, '$1' + ',' + '$2');
13098             }
13099             return "$" + whole + sub ;
13100         },
13101         
13102         /**
13103          * Parse a value into a formatted date using the specified format pattern.
13104          * @param {Mixed} value The value to format
13105          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13106          * @return {String} The formatted date string
13107          */
13108         date : function(v, format){
13109             if(!v){
13110                 return "";
13111             }
13112             if(!(v instanceof Date)){
13113                 v = new Date(Date.parse(v));
13114             }
13115             return v.dateFormat(format || "m/d/Y");
13116         },
13117
13118         /**
13119          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13120          * @param {String} format Any valid date format string
13121          * @return {Function} The date formatting function
13122          */
13123         dateRenderer : function(format){
13124             return function(v){
13125                 return Roo.util.Format.date(v, format);  
13126             };
13127         },
13128
13129         // private
13130         stripTagsRE : /<\/?[^>]+>/gi,
13131         
13132         /**
13133          * Strips all HTML tags
13134          * @param {Mixed} value The text from which to strip tags
13135          * @return {String} The stripped text
13136          */
13137         stripTags : function(v){
13138             return !v ? v : String(v).replace(this.stripTagsRE, "");
13139         }
13140     };
13141 }();/*
13142  * Based on:
13143  * Ext JS Library 1.1.1
13144  * Copyright(c) 2006-2007, Ext JS, LLC.
13145  *
13146  * Originally Released Under LGPL - original licence link has changed is not relivant.
13147  *
13148  * Fork - LGPL
13149  * <script type="text/javascript">
13150  */
13151
13152
13153  
13154
13155 /**
13156  * @class Roo.MasterTemplate
13157  * @extends Roo.Template
13158  * Provides a template that can have child templates. The syntax is:
13159 <pre><code>
13160 var t = new Roo.MasterTemplate(
13161         '&lt;select name="{name}"&gt;',
13162                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13163         '&lt;/select&gt;'
13164 );
13165 t.add('options', {value: 'foo', text: 'bar'});
13166 // or you can add multiple child elements in one shot
13167 t.addAll('options', [
13168     {value: 'foo', text: 'bar'},
13169     {value: 'foo2', text: 'bar2'},
13170     {value: 'foo3', text: 'bar3'}
13171 ]);
13172 // then append, applying the master template values
13173 t.append('my-form', {name: 'my-select'});
13174 </code></pre>
13175 * A name attribute for the child template is not required if you have only one child
13176 * template or you want to refer to them by index.
13177  */
13178 Roo.MasterTemplate = function(){
13179     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13180     this.originalHtml = this.html;
13181     var st = {};
13182     var m, re = this.subTemplateRe;
13183     re.lastIndex = 0;
13184     var subIndex = 0;
13185     while(m = re.exec(this.html)){
13186         var name = m[1], content = m[2];
13187         st[subIndex] = {
13188             name: name,
13189             index: subIndex,
13190             buffer: [],
13191             tpl : new Roo.Template(content)
13192         };
13193         if(name){
13194             st[name] = st[subIndex];
13195         }
13196         st[subIndex].tpl.compile();
13197         st[subIndex].tpl.call = this.call.createDelegate(this);
13198         subIndex++;
13199     }
13200     this.subCount = subIndex;
13201     this.subs = st;
13202 };
13203 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13204     /**
13205     * The regular expression used to match sub templates
13206     * @type RegExp
13207     * @property
13208     */
13209     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13210
13211     /**
13212      * Applies the passed values to a child template.
13213      * @param {String/Number} name (optional) The name or index of the child template
13214      * @param {Array/Object} values The values to be applied to the template
13215      * @return {MasterTemplate} this
13216      */
13217      add : function(name, values){
13218         if(arguments.length == 1){
13219             values = arguments[0];
13220             name = 0;
13221         }
13222         var s = this.subs[name];
13223         s.buffer[s.buffer.length] = s.tpl.apply(values);
13224         return this;
13225     },
13226
13227     /**
13228      * Applies all the passed values to a child template.
13229      * @param {String/Number} name (optional) The name or index of the child template
13230      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13231      * @param {Boolean} reset (optional) True to reset the template first
13232      * @return {MasterTemplate} this
13233      */
13234     fill : function(name, values, reset){
13235         var a = arguments;
13236         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13237             values = a[0];
13238             name = 0;
13239             reset = a[1];
13240         }
13241         if(reset){
13242             this.reset();
13243         }
13244         for(var i = 0, len = values.length; i < len; i++){
13245             this.add(name, values[i]);
13246         }
13247         return this;
13248     },
13249
13250     /**
13251      * Resets the template for reuse
13252      * @return {MasterTemplate} this
13253      */
13254      reset : function(){
13255         var s = this.subs;
13256         for(var i = 0; i < this.subCount; i++){
13257             s[i].buffer = [];
13258         }
13259         return this;
13260     },
13261
13262     applyTemplate : function(values){
13263         var s = this.subs;
13264         var replaceIndex = -1;
13265         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13266             return s[++replaceIndex].buffer.join("");
13267         });
13268         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13269     },
13270
13271     apply : function(){
13272         return this.applyTemplate.apply(this, arguments);
13273     },
13274
13275     compile : function(){return this;}
13276 });
13277
13278 /**
13279  * Alias for fill().
13280  * @method
13281  */
13282 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13283  /**
13284  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13285  * var tpl = Roo.MasterTemplate.from('element-id');
13286  * @param {String/HTMLElement} el
13287  * @param {Object} config
13288  * @static
13289  */
13290 Roo.MasterTemplate.from = function(el, config){
13291     el = Roo.getDom(el);
13292     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13293 };/*
13294  * Based on:
13295  * Ext JS Library 1.1.1
13296  * Copyright(c) 2006-2007, Ext JS, LLC.
13297  *
13298  * Originally Released Under LGPL - original licence link has changed is not relivant.
13299  *
13300  * Fork - LGPL
13301  * <script type="text/javascript">
13302  */
13303
13304  
13305 /**
13306  * @class Roo.util.CSS
13307  * Utility class for manipulating CSS rules
13308  * @singleton
13309  */
13310 Roo.util.CSS = function(){
13311         var rules = null;
13312         var doc = document;
13313
13314     var camelRe = /(-[a-z])/gi;
13315     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13316
13317    return {
13318    /**
13319     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13320     * tag and appended to the HEAD of the document.
13321     * @param {String|Object} cssText The text containing the css rules
13322     * @param {String} id An id to add to the stylesheet for later removal
13323     * @return {StyleSheet}
13324     */
13325     createStyleSheet : function(cssText, id){
13326         var ss;
13327         var head = doc.getElementsByTagName("head")[0];
13328         var nrules = doc.createElement("style");
13329         nrules.setAttribute("type", "text/css");
13330         if(id){
13331             nrules.setAttribute("id", id);
13332         }
13333         if (typeof(cssText) != 'string') {
13334             // support object maps..
13335             // not sure if this a good idea.. 
13336             // perhaps it should be merged with the general css handling
13337             // and handle js style props.
13338             var cssTextNew = [];
13339             for(var n in cssText) {
13340                 var citems = [];
13341                 for(var k in cssText[n]) {
13342                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13343                 }
13344                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13345                 
13346             }
13347             cssText = cssTextNew.join("\n");
13348             
13349         }
13350        
13351        
13352        if(Roo.isIE){
13353            head.appendChild(nrules);
13354            ss = nrules.styleSheet;
13355            ss.cssText = cssText;
13356        }else{
13357            try{
13358                 nrules.appendChild(doc.createTextNode(cssText));
13359            }catch(e){
13360                nrules.cssText = cssText; 
13361            }
13362            head.appendChild(nrules);
13363            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13364        }
13365        this.cacheStyleSheet(ss);
13366        return ss;
13367    },
13368
13369    /**
13370     * Removes a style or link tag by id
13371     * @param {String} id The id of the tag
13372     */
13373    removeStyleSheet : function(id){
13374        var existing = doc.getElementById(id);
13375        if(existing){
13376            existing.parentNode.removeChild(existing);
13377        }
13378    },
13379
13380    /**
13381     * Dynamically swaps an existing stylesheet reference for a new one
13382     * @param {String} id The id of an existing link tag to remove
13383     * @param {String} url The href of the new stylesheet to include
13384     */
13385    swapStyleSheet : function(id, url){
13386        this.removeStyleSheet(id);
13387        var ss = doc.createElement("link");
13388        ss.setAttribute("rel", "stylesheet");
13389        ss.setAttribute("type", "text/css");
13390        ss.setAttribute("id", id);
13391        ss.setAttribute("href", url);
13392        doc.getElementsByTagName("head")[0].appendChild(ss);
13393    },
13394    
13395    /**
13396     * Refresh the rule cache if you have dynamically added stylesheets
13397     * @return {Object} An object (hash) of rules indexed by selector
13398     */
13399    refreshCache : function(){
13400        return this.getRules(true);
13401    },
13402
13403    // private
13404    cacheStyleSheet : function(stylesheet){
13405        if(!rules){
13406            rules = {};
13407        }
13408        try{// try catch for cross domain access issue
13409            var ssRules = stylesheet.cssRules || stylesheet.rules;
13410            for(var j = ssRules.length-1; j >= 0; --j){
13411                rules[ssRules[j].selectorText] = ssRules[j];
13412            }
13413        }catch(e){}
13414    },
13415    
13416    /**
13417     * Gets all css rules for the document
13418     * @param {Boolean} refreshCache true to refresh the internal cache
13419     * @return {Object} An object (hash) of rules indexed by selector
13420     */
13421    getRules : function(refreshCache){
13422                 if(rules == null || refreshCache){
13423                         rules = {};
13424                         var ds = doc.styleSheets;
13425                         for(var i =0, len = ds.length; i < len; i++){
13426                             try{
13427                         this.cacheStyleSheet(ds[i]);
13428                     }catch(e){} 
13429                 }
13430                 }
13431                 return rules;
13432         },
13433         
13434         /**
13435     * Gets an an individual CSS rule by selector(s)
13436     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13437     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13438     * @return {CSSRule} The CSS rule or null if one is not found
13439     */
13440    getRule : function(selector, refreshCache){
13441                 var rs = this.getRules(refreshCache);
13442                 if(!(selector instanceof Array)){
13443                     return rs[selector];
13444                 }
13445                 for(var i = 0; i < selector.length; i++){
13446                         if(rs[selector[i]]){
13447                                 return rs[selector[i]];
13448                         }
13449                 }
13450                 return null;
13451         },
13452         
13453         
13454         /**
13455     * Updates a rule property
13456     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13457     * @param {String} property The css property
13458     * @param {String} value The new value for the property
13459     * @return {Boolean} true If a rule was found and updated
13460     */
13461    updateRule : function(selector, property, value){
13462                 if(!(selector instanceof Array)){
13463                         var rule = this.getRule(selector);
13464                         if(rule){
13465                                 rule.style[property.replace(camelRe, camelFn)] = value;
13466                                 return true;
13467                         }
13468                 }else{
13469                         for(var i = 0; i < selector.length; i++){
13470                                 if(this.updateRule(selector[i], property, value)){
13471                                         return true;
13472                                 }
13473                         }
13474                 }
13475                 return false;
13476         }
13477    };   
13478 }();/*
13479  * Based on:
13480  * Ext JS Library 1.1.1
13481  * Copyright(c) 2006-2007, Ext JS, LLC.
13482  *
13483  * Originally Released Under LGPL - original licence link has changed is not relivant.
13484  *
13485  * Fork - LGPL
13486  * <script type="text/javascript">
13487  */
13488
13489  
13490
13491 /**
13492  * @class Roo.util.ClickRepeater
13493  * @extends Roo.util.Observable
13494  * 
13495  * A wrapper class which can be applied to any element. Fires a "click" event while the
13496  * mouse is pressed. The interval between firings may be specified in the config but
13497  * defaults to 10 milliseconds.
13498  * 
13499  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13500  * 
13501  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13502  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13503  * Similar to an autorepeat key delay.
13504  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13505  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13506  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13507  *           "interval" and "delay" are ignored. "immediate" is honored.
13508  * @cfg {Boolean} preventDefault True to prevent the default click event
13509  * @cfg {Boolean} stopDefault True to stop the default click event
13510  * 
13511  * @history
13512  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13513  *     2007-02-02 jvs Renamed to ClickRepeater
13514  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13515  *
13516  *  @constructor
13517  * @param {String/HTMLElement/Element} el The element to listen on
13518  * @param {Object} config
13519  **/
13520 Roo.util.ClickRepeater = function(el, config)
13521 {
13522     this.el = Roo.get(el);
13523     this.el.unselectable();
13524
13525     Roo.apply(this, config);
13526
13527     this.addEvents({
13528     /**
13529      * @event mousedown
13530      * Fires when the mouse button is depressed.
13531      * @param {Roo.util.ClickRepeater} this
13532      */
13533         "mousedown" : true,
13534     /**
13535      * @event click
13536      * Fires on a specified interval during the time the element is pressed.
13537      * @param {Roo.util.ClickRepeater} this
13538      */
13539         "click" : true,
13540     /**
13541      * @event mouseup
13542      * Fires when the mouse key is released.
13543      * @param {Roo.util.ClickRepeater} this
13544      */
13545         "mouseup" : true
13546     });
13547
13548     this.el.on("mousedown", this.handleMouseDown, this);
13549     if(this.preventDefault || this.stopDefault){
13550         this.el.on("click", function(e){
13551             if(this.preventDefault){
13552                 e.preventDefault();
13553             }
13554             if(this.stopDefault){
13555                 e.stopEvent();
13556             }
13557         }, this);
13558     }
13559
13560     // allow inline handler
13561     if(this.handler){
13562         this.on("click", this.handler,  this.scope || this);
13563     }
13564
13565     Roo.util.ClickRepeater.superclass.constructor.call(this);
13566 };
13567
13568 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13569     interval : 20,
13570     delay: 250,
13571     preventDefault : true,
13572     stopDefault : false,
13573     timer : 0,
13574
13575     // private
13576     handleMouseDown : function(){
13577         clearTimeout(this.timer);
13578         this.el.blur();
13579         if(this.pressClass){
13580             this.el.addClass(this.pressClass);
13581         }
13582         this.mousedownTime = new Date();
13583
13584         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13585         this.el.on("mouseout", this.handleMouseOut, this);
13586
13587         this.fireEvent("mousedown", this);
13588         this.fireEvent("click", this);
13589         
13590         this.timer = this.click.defer(this.delay || this.interval, this);
13591     },
13592
13593     // private
13594     click : function(){
13595         this.fireEvent("click", this);
13596         this.timer = this.click.defer(this.getInterval(), this);
13597     },
13598
13599     // private
13600     getInterval: function(){
13601         if(!this.accelerate){
13602             return this.interval;
13603         }
13604         var pressTime = this.mousedownTime.getElapsed();
13605         if(pressTime < 500){
13606             return 400;
13607         }else if(pressTime < 1700){
13608             return 320;
13609         }else if(pressTime < 2600){
13610             return 250;
13611         }else if(pressTime < 3500){
13612             return 180;
13613         }else if(pressTime < 4400){
13614             return 140;
13615         }else if(pressTime < 5300){
13616             return 80;
13617         }else if(pressTime < 6200){
13618             return 50;
13619         }else{
13620             return 10;
13621         }
13622     },
13623
13624     // private
13625     handleMouseOut : function(){
13626         clearTimeout(this.timer);
13627         if(this.pressClass){
13628             this.el.removeClass(this.pressClass);
13629         }
13630         this.el.on("mouseover", this.handleMouseReturn, this);
13631     },
13632
13633     // private
13634     handleMouseReturn : function(){
13635         this.el.un("mouseover", this.handleMouseReturn);
13636         if(this.pressClass){
13637             this.el.addClass(this.pressClass);
13638         }
13639         this.click();
13640     },
13641
13642     // private
13643     handleMouseUp : function(){
13644         clearTimeout(this.timer);
13645         this.el.un("mouseover", this.handleMouseReturn);
13646         this.el.un("mouseout", this.handleMouseOut);
13647         Roo.get(document).un("mouseup", this.handleMouseUp);
13648         this.el.removeClass(this.pressClass);
13649         this.fireEvent("mouseup", this);
13650     }
13651 });/*
13652  * Based on:
13653  * Ext JS Library 1.1.1
13654  * Copyright(c) 2006-2007, Ext JS, LLC.
13655  *
13656  * Originally Released Under LGPL - original licence link has changed is not relivant.
13657  *
13658  * Fork - LGPL
13659  * <script type="text/javascript">
13660  */
13661
13662  
13663 /**
13664  * @class Roo.KeyNav
13665  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13666  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13667  * way to implement custom navigation schemes for any UI component.</p>
13668  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13669  * pageUp, pageDown, del, home, end.  Usage:</p>
13670  <pre><code>
13671 var nav = new Roo.KeyNav("my-element", {
13672     "left" : function(e){
13673         this.moveLeft(e.ctrlKey);
13674     },
13675     "right" : function(e){
13676         this.moveRight(e.ctrlKey);
13677     },
13678     "enter" : function(e){
13679         this.save();
13680     },
13681     scope : this
13682 });
13683 </code></pre>
13684  * @constructor
13685  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13686  * @param {Object} config The config
13687  */
13688 Roo.KeyNav = function(el, config){
13689     this.el = Roo.get(el);
13690     Roo.apply(this, config);
13691     if(!this.disabled){
13692         this.disabled = true;
13693         this.enable();
13694     }
13695 };
13696
13697 Roo.KeyNav.prototype = {
13698     /**
13699      * @cfg {Boolean} disabled
13700      * True to disable this KeyNav instance (defaults to false)
13701      */
13702     disabled : false,
13703     /**
13704      * @cfg {String} defaultEventAction
13705      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13706      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13707      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13708      */
13709     defaultEventAction: "stopEvent",
13710     /**
13711      * @cfg {Boolean} forceKeyDown
13712      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13713      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13714      * handle keydown instead of keypress.
13715      */
13716     forceKeyDown : false,
13717
13718     // private
13719     prepareEvent : function(e){
13720         var k = e.getKey();
13721         var h = this.keyToHandler[k];
13722         //if(h && this[h]){
13723         //    e.stopPropagation();
13724         //}
13725         if(Roo.isSafari && h && k >= 37 && k <= 40){
13726             e.stopEvent();
13727         }
13728     },
13729
13730     // private
13731     relay : function(e){
13732         var k = e.getKey();
13733         var h = this.keyToHandler[k];
13734         if(h && this[h]){
13735             if(this.doRelay(e, this[h], h) !== true){
13736                 e[this.defaultEventAction]();
13737             }
13738         }
13739     },
13740
13741     // private
13742     doRelay : function(e, h, hname){
13743         return h.call(this.scope || this, e);
13744     },
13745
13746     // possible handlers
13747     enter : false,
13748     left : false,
13749     right : false,
13750     up : false,
13751     down : false,
13752     tab : false,
13753     esc : false,
13754     pageUp : false,
13755     pageDown : false,
13756     del : false,
13757     home : false,
13758     end : false,
13759
13760     // quick lookup hash
13761     keyToHandler : {
13762         37 : "left",
13763         39 : "right",
13764         38 : "up",
13765         40 : "down",
13766         33 : "pageUp",
13767         34 : "pageDown",
13768         46 : "del",
13769         36 : "home",
13770         35 : "end",
13771         13 : "enter",
13772         27 : "esc",
13773         9  : "tab"
13774     },
13775
13776         /**
13777          * Enable this KeyNav
13778          */
13779         enable: function(){
13780                 if(this.disabled){
13781             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13782             // the EventObject will normalize Safari automatically
13783             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13784                 this.el.on("keydown", this.relay,  this);
13785             }else{
13786                 this.el.on("keydown", this.prepareEvent,  this);
13787                 this.el.on("keypress", this.relay,  this);
13788             }
13789                     this.disabled = false;
13790                 }
13791         },
13792
13793         /**
13794          * Disable this KeyNav
13795          */
13796         disable: function(){
13797                 if(!this.disabled){
13798                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13799                 this.el.un("keydown", this.relay);
13800             }else{
13801                 this.el.un("keydown", this.prepareEvent);
13802                 this.el.un("keypress", this.relay);
13803             }
13804                     this.disabled = true;
13805                 }
13806         }
13807 };/*
13808  * Based on:
13809  * Ext JS Library 1.1.1
13810  * Copyright(c) 2006-2007, Ext JS, LLC.
13811  *
13812  * Originally Released Under LGPL - original licence link has changed is not relivant.
13813  *
13814  * Fork - LGPL
13815  * <script type="text/javascript">
13816  */
13817
13818  
13819 /**
13820  * @class Roo.KeyMap
13821  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13822  * The constructor accepts the same config object as defined by {@link #addBinding}.
13823  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13824  * combination it will call the function with this signature (if the match is a multi-key
13825  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13826  * A KeyMap can also handle a string representation of keys.<br />
13827  * Usage:
13828  <pre><code>
13829 // map one key by key code
13830 var map = new Roo.KeyMap("my-element", {
13831     key: 13, // or Roo.EventObject.ENTER
13832     fn: myHandler,
13833     scope: myObject
13834 });
13835
13836 // map multiple keys to one action by string
13837 var map = new Roo.KeyMap("my-element", {
13838     key: "a\r\n\t",
13839     fn: myHandler,
13840     scope: myObject
13841 });
13842
13843 // map multiple keys to multiple actions by strings and array of codes
13844 var map = new Roo.KeyMap("my-element", [
13845     {
13846         key: [10,13],
13847         fn: function(){ alert("Return was pressed"); }
13848     }, {
13849         key: "abc",
13850         fn: function(){ alert('a, b or c was pressed'); }
13851     }, {
13852         key: "\t",
13853         ctrl:true,
13854         shift:true,
13855         fn: function(){ alert('Control + shift + tab was pressed.'); }
13856     }
13857 ]);
13858 </code></pre>
13859  * <b>Note: A KeyMap starts enabled</b>
13860  * @constructor
13861  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13862  * @param {Object} config The config (see {@link #addBinding})
13863  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13864  */
13865 Roo.KeyMap = function(el, config, eventName){
13866     this.el  = Roo.get(el);
13867     this.eventName = eventName || "keydown";
13868     this.bindings = [];
13869     if(config){
13870         this.addBinding(config);
13871     }
13872     this.enable();
13873 };
13874
13875 Roo.KeyMap.prototype = {
13876     /**
13877      * True to stop the event from bubbling and prevent the default browser action if the
13878      * key was handled by the KeyMap (defaults to false)
13879      * @type Boolean
13880      */
13881     stopEvent : false,
13882
13883     /**
13884      * Add a new binding to this KeyMap. The following config object properties are supported:
13885      * <pre>
13886 Property    Type             Description
13887 ----------  ---------------  ----------------------------------------------------------------------
13888 key         String/Array     A single keycode or an array of keycodes to handle
13889 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13890 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13891 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13892 fn          Function         The function to call when KeyMap finds the expected key combination
13893 scope       Object           The scope of the callback function
13894 </pre>
13895      *
13896      * Usage:
13897      * <pre><code>
13898 // Create a KeyMap
13899 var map = new Roo.KeyMap(document, {
13900     key: Roo.EventObject.ENTER,
13901     fn: handleKey,
13902     scope: this
13903 });
13904
13905 //Add a new binding to the existing KeyMap later
13906 map.addBinding({
13907     key: 'abc',
13908     shift: true,
13909     fn: handleKey,
13910     scope: this
13911 });
13912 </code></pre>
13913      * @param {Object/Array} config A single KeyMap config or an array of configs
13914      */
13915         addBinding : function(config){
13916         if(config instanceof Array){
13917             for(var i = 0, len = config.length; i < len; i++){
13918                 this.addBinding(config[i]);
13919             }
13920             return;
13921         }
13922         var keyCode = config.key,
13923             shift = config.shift, 
13924             ctrl = config.ctrl, 
13925             alt = config.alt,
13926             fn = config.fn,
13927             scope = config.scope;
13928         if(typeof keyCode == "string"){
13929             var ks = [];
13930             var keyString = keyCode.toUpperCase();
13931             for(var j = 0, len = keyString.length; j < len; j++){
13932                 ks.push(keyString.charCodeAt(j));
13933             }
13934             keyCode = ks;
13935         }
13936         var keyArray = keyCode instanceof Array;
13937         var handler = function(e){
13938             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13939                 var k = e.getKey();
13940                 if(keyArray){
13941                     for(var i = 0, len = keyCode.length; i < len; i++){
13942                         if(keyCode[i] == k){
13943                           if(this.stopEvent){
13944                               e.stopEvent();
13945                           }
13946                           fn.call(scope || window, k, e);
13947                           return;
13948                         }
13949                     }
13950                 }else{
13951                     if(k == keyCode){
13952                         if(this.stopEvent){
13953                            e.stopEvent();
13954                         }
13955                         fn.call(scope || window, k, e);
13956                     }
13957                 }
13958             }
13959         };
13960         this.bindings.push(handler);  
13961         },
13962
13963     /**
13964      * Shorthand for adding a single key listener
13965      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13966      * following options:
13967      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13968      * @param {Function} fn The function to call
13969      * @param {Object} scope (optional) The scope of the function
13970      */
13971     on : function(key, fn, scope){
13972         var keyCode, shift, ctrl, alt;
13973         if(typeof key == "object" && !(key instanceof Array)){
13974             keyCode = key.key;
13975             shift = key.shift;
13976             ctrl = key.ctrl;
13977             alt = key.alt;
13978         }else{
13979             keyCode = key;
13980         }
13981         this.addBinding({
13982             key: keyCode,
13983             shift: shift,
13984             ctrl: ctrl,
13985             alt: alt,
13986             fn: fn,
13987             scope: scope
13988         })
13989     },
13990
13991     // private
13992     handleKeyDown : function(e){
13993             if(this.enabled){ //just in case
13994             var b = this.bindings;
13995             for(var i = 0, len = b.length; i < len; i++){
13996                 b[i].call(this, e);
13997             }
13998             }
13999         },
14000         
14001         /**
14002          * Returns true if this KeyMap is enabled
14003          * @return {Boolean} 
14004          */
14005         isEnabled : function(){
14006             return this.enabled;  
14007         },
14008         
14009         /**
14010          * Enables this KeyMap
14011          */
14012         enable: function(){
14013                 if(!this.enabled){
14014                     this.el.on(this.eventName, this.handleKeyDown, this);
14015                     this.enabled = true;
14016                 }
14017         },
14018
14019         /**
14020          * Disable this KeyMap
14021          */
14022         disable: function(){
14023                 if(this.enabled){
14024                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14025                     this.enabled = false;
14026                 }
14027         }
14028 };/*
14029  * Based on:
14030  * Ext JS Library 1.1.1
14031  * Copyright(c) 2006-2007, Ext JS, LLC.
14032  *
14033  * Originally Released Under LGPL - original licence link has changed is not relivant.
14034  *
14035  * Fork - LGPL
14036  * <script type="text/javascript">
14037  */
14038
14039  
14040 /**
14041  * @class Roo.util.TextMetrics
14042  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14043  * wide, in pixels, a given block of text will be.
14044  * @singleton
14045  */
14046 Roo.util.TextMetrics = function(){
14047     var shared;
14048     return {
14049         /**
14050          * Measures the size of the specified text
14051          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14052          * that can affect the size of the rendered text
14053          * @param {String} text The text to measure
14054          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14055          * in order to accurately measure the text height
14056          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14057          */
14058         measure : function(el, text, fixedWidth){
14059             if(!shared){
14060                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14061             }
14062             shared.bind(el);
14063             shared.setFixedWidth(fixedWidth || 'auto');
14064             return shared.getSize(text);
14065         },
14066
14067         /**
14068          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14069          * the overhead of multiple calls to initialize the style properties on each measurement.
14070          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14071          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14072          * in order to accurately measure the text height
14073          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14074          */
14075         createInstance : function(el, fixedWidth){
14076             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14077         }
14078     };
14079 }();
14080
14081  
14082
14083 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14084     var ml = new Roo.Element(document.createElement('div'));
14085     document.body.appendChild(ml.dom);
14086     ml.position('absolute');
14087     ml.setLeftTop(-1000, -1000);
14088     ml.hide();
14089
14090     if(fixedWidth){
14091         ml.setWidth(fixedWidth);
14092     }
14093      
14094     var instance = {
14095         /**
14096          * Returns the size of the specified text based on the internal element's style and width properties
14097          * @memberOf Roo.util.TextMetrics.Instance#
14098          * @param {String} text The text to measure
14099          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14100          */
14101         getSize : function(text){
14102             ml.update(text);
14103             var s = ml.getSize();
14104             ml.update('');
14105             return s;
14106         },
14107
14108         /**
14109          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14110          * that can affect the size of the rendered text
14111          * @memberOf Roo.util.TextMetrics.Instance#
14112          * @param {String/HTMLElement} el The element, dom node or id
14113          */
14114         bind : function(el){
14115             ml.setStyle(
14116                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14117             );
14118         },
14119
14120         /**
14121          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14122          * to set a fixed width in order to accurately measure the text height.
14123          * @memberOf Roo.util.TextMetrics.Instance#
14124          * @param {Number} width The width to set on the element
14125          */
14126         setFixedWidth : function(width){
14127             ml.setWidth(width);
14128         },
14129
14130         /**
14131          * Returns the measured width of the specified text
14132          * @memberOf Roo.util.TextMetrics.Instance#
14133          * @param {String} text The text to measure
14134          * @return {Number} width The width in pixels
14135          */
14136         getWidth : function(text){
14137             ml.dom.style.width = 'auto';
14138             return this.getSize(text).width;
14139         },
14140
14141         /**
14142          * Returns the measured height of the specified text.  For multiline text, be sure to call
14143          * {@link #setFixedWidth} if necessary.
14144          * @memberOf Roo.util.TextMetrics.Instance#
14145          * @param {String} text The text to measure
14146          * @return {Number} height The height in pixels
14147          */
14148         getHeight : function(text){
14149             return this.getSize(text).height;
14150         }
14151     };
14152
14153     instance.bind(bindTo);
14154
14155     return instance;
14156 };
14157
14158 // backwards compat
14159 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14160  * Based on:
14161  * Ext JS Library 1.1.1
14162  * Copyright(c) 2006-2007, Ext JS, LLC.
14163  *
14164  * Originally Released Under LGPL - original licence link has changed is not relivant.
14165  *
14166  * Fork - LGPL
14167  * <script type="text/javascript">
14168  */
14169
14170 /**
14171  * @class Roo.state.Provider
14172  * Abstract base class for state provider implementations. This class provides methods
14173  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14174  * Provider interface.
14175  */
14176 Roo.state.Provider = function(){
14177     /**
14178      * @event statechange
14179      * Fires when a state change occurs.
14180      * @param {Provider} this This state provider
14181      * @param {String} key The state key which was changed
14182      * @param {String} value The encoded value for the state
14183      */
14184     this.addEvents({
14185         "statechange": true
14186     });
14187     this.state = {};
14188     Roo.state.Provider.superclass.constructor.call(this);
14189 };
14190 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14191     /**
14192      * Returns the current value for a key
14193      * @param {String} name The key name
14194      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14195      * @return {Mixed} The state data
14196      */
14197     get : function(name, defaultValue){
14198         return typeof this.state[name] == "undefined" ?
14199             defaultValue : this.state[name];
14200     },
14201     
14202     /**
14203      * Clears a value from the state
14204      * @param {String} name The key name
14205      */
14206     clear : function(name){
14207         delete this.state[name];
14208         this.fireEvent("statechange", this, name, null);
14209     },
14210     
14211     /**
14212      * Sets the value for a key
14213      * @param {String} name The key name
14214      * @param {Mixed} value The value to set
14215      */
14216     set : function(name, value){
14217         this.state[name] = value;
14218         this.fireEvent("statechange", this, name, value);
14219     },
14220     
14221     /**
14222      * Decodes a string previously encoded with {@link #encodeValue}.
14223      * @param {String} value The value to decode
14224      * @return {Mixed} The decoded value
14225      */
14226     decodeValue : function(cookie){
14227         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14228         var matches = re.exec(unescape(cookie));
14229         if(!matches || !matches[1]) return; // non state cookie
14230         var type = matches[1];
14231         var v = matches[2];
14232         switch(type){
14233             case "n":
14234                 return parseFloat(v);
14235             case "d":
14236                 return new Date(Date.parse(v));
14237             case "b":
14238                 return (v == "1");
14239             case "a":
14240                 var all = [];
14241                 var values = v.split("^");
14242                 for(var i = 0, len = values.length; i < len; i++){
14243                     all.push(this.decodeValue(values[i]));
14244                 }
14245                 return all;
14246            case "o":
14247                 var all = {};
14248                 var values = v.split("^");
14249                 for(var i = 0, len = values.length; i < len; i++){
14250                     var kv = values[i].split("=");
14251                     all[kv[0]] = this.decodeValue(kv[1]);
14252                 }
14253                 return all;
14254            default:
14255                 return v;
14256         }
14257     },
14258     
14259     /**
14260      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14261      * @param {Mixed} value The value to encode
14262      * @return {String} The encoded value
14263      */
14264     encodeValue : function(v){
14265         var enc;
14266         if(typeof v == "number"){
14267             enc = "n:" + v;
14268         }else if(typeof v == "boolean"){
14269             enc = "b:" + (v ? "1" : "0");
14270         }else if(v instanceof Date){
14271             enc = "d:" + v.toGMTString();
14272         }else if(v instanceof Array){
14273             var flat = "";
14274             for(var i = 0, len = v.length; i < len; i++){
14275                 flat += this.encodeValue(v[i]);
14276                 if(i != len-1) flat += "^";
14277             }
14278             enc = "a:" + flat;
14279         }else if(typeof v == "object"){
14280             var flat = "";
14281             for(var key in v){
14282                 if(typeof v[key] != "function"){
14283                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14284                 }
14285             }
14286             enc = "o:" + flat.substring(0, flat.length-1);
14287         }else{
14288             enc = "s:" + v;
14289         }
14290         return escape(enc);        
14291     }
14292 });
14293
14294 /*
14295  * Based on:
14296  * Ext JS Library 1.1.1
14297  * Copyright(c) 2006-2007, Ext JS, LLC.
14298  *
14299  * Originally Released Under LGPL - original licence link has changed is not relivant.
14300  *
14301  * Fork - LGPL
14302  * <script type="text/javascript">
14303  */
14304 /**
14305  * @class Roo.state.Manager
14306  * This is the global state manager. By default all components that are "state aware" check this class
14307  * for state information if you don't pass them a custom state provider. In order for this class
14308  * to be useful, it must be initialized with a provider when your application initializes.
14309  <pre><code>
14310 // in your initialization function
14311 init : function(){
14312    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14313    ...
14314    // supposed you have a {@link Roo.BorderLayout}
14315    var layout = new Roo.BorderLayout(...);
14316    layout.restoreState();
14317    // or a {Roo.BasicDialog}
14318    var dialog = new Roo.BasicDialog(...);
14319    dialog.restoreState();
14320  </code></pre>
14321  * @singleton
14322  */
14323 Roo.state.Manager = function(){
14324     var provider = new Roo.state.Provider();
14325     
14326     return {
14327         /**
14328          * Configures the default state provider for your application
14329          * @param {Provider} stateProvider The state provider to set
14330          */
14331         setProvider : function(stateProvider){
14332             provider = stateProvider;
14333         },
14334         
14335         /**
14336          * Returns the current value for a key
14337          * @param {String} name The key name
14338          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14339          * @return {Mixed} The state data
14340          */
14341         get : function(key, defaultValue){
14342             return provider.get(key, defaultValue);
14343         },
14344         
14345         /**
14346          * Sets the value for a key
14347          * @param {String} name The key name
14348          * @param {Mixed} value The state data
14349          */
14350          set : function(key, value){
14351             provider.set(key, value);
14352         },
14353         
14354         /**
14355          * Clears a value from the state
14356          * @param {String} name The key name
14357          */
14358         clear : function(key){
14359             provider.clear(key);
14360         },
14361         
14362         /**
14363          * Gets the currently configured state provider
14364          * @return {Provider} The state provider
14365          */
14366         getProvider : function(){
14367             return provider;
14368         }
14369     };
14370 }();
14371 /*
14372  * Based on:
14373  * Ext JS Library 1.1.1
14374  * Copyright(c) 2006-2007, Ext JS, LLC.
14375  *
14376  * Originally Released Under LGPL - original licence link has changed is not relivant.
14377  *
14378  * Fork - LGPL
14379  * <script type="text/javascript">
14380  */
14381 /**
14382  * @class Roo.state.CookieProvider
14383  * @extends Roo.state.Provider
14384  * The default Provider implementation which saves state via cookies.
14385  * <br />Usage:
14386  <pre><code>
14387    var cp = new Roo.state.CookieProvider({
14388        path: "/cgi-bin/",
14389        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14390        domain: "roojs.com"
14391    })
14392    Roo.state.Manager.setProvider(cp);
14393  </code></pre>
14394  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14395  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14396  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14397  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14398  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14399  * domain the page is running on including the 'www' like 'www.roojs.com')
14400  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14401  * @constructor
14402  * Create a new CookieProvider
14403  * @param {Object} config The configuration object
14404  */
14405 Roo.state.CookieProvider = function(config){
14406     Roo.state.CookieProvider.superclass.constructor.call(this);
14407     this.path = "/";
14408     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14409     this.domain = null;
14410     this.secure = false;
14411     Roo.apply(this, config);
14412     this.state = this.readCookies();
14413 };
14414
14415 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14416     // private
14417     set : function(name, value){
14418         if(typeof value == "undefined" || value === null){
14419             this.clear(name);
14420             return;
14421         }
14422         this.setCookie(name, value);
14423         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14424     },
14425
14426     // private
14427     clear : function(name){
14428         this.clearCookie(name);
14429         Roo.state.CookieProvider.superclass.clear.call(this, name);
14430     },
14431
14432     // private
14433     readCookies : function(){
14434         var cookies = {};
14435         var c = document.cookie + ";";
14436         var re = /\s?(.*?)=(.*?);/g;
14437         var matches;
14438         while((matches = re.exec(c)) != null){
14439             var name = matches[1];
14440             var value = matches[2];
14441             if(name && name.substring(0,3) == "ys-"){
14442                 cookies[name.substr(3)] = this.decodeValue(value);
14443             }
14444         }
14445         return cookies;
14446     },
14447
14448     // private
14449     setCookie : function(name, value){
14450         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14451            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14452            ((this.path == null) ? "" : ("; path=" + this.path)) +
14453            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14454            ((this.secure == true) ? "; secure" : "");
14455     },
14456
14457     // private
14458     clearCookie : function(name){
14459         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14460            ((this.path == null) ? "" : ("; path=" + this.path)) +
14461            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14462            ((this.secure == true) ? "; secure" : "");
14463     }
14464 });/*
14465  * Based on:
14466  * Ext JS Library 1.1.1
14467  * Copyright(c) 2006-2007, Ext JS, LLC.
14468  *
14469  * Originally Released Under LGPL - original licence link has changed is not relivant.
14470  *
14471  * Fork - LGPL
14472  * <script type="text/javascript">
14473  */
14474
14475
14476
14477 /*
14478  * These classes are derivatives of the similarly named classes in the YUI Library.
14479  * The original license:
14480  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14481  * Code licensed under the BSD License:
14482  * http://developer.yahoo.net/yui/license.txt
14483  */
14484
14485 (function() {
14486
14487 var Event=Roo.EventManager;
14488 var Dom=Roo.lib.Dom;
14489
14490 /**
14491  * @class Roo.dd.DragDrop
14492  * @extends Roo.util.Observable
14493  * Defines the interface and base operation of items that that can be
14494  * dragged or can be drop targets.  It was designed to be extended, overriding
14495  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14496  * Up to three html elements can be associated with a DragDrop instance:
14497  * <ul>
14498  * <li>linked element: the element that is passed into the constructor.
14499  * This is the element which defines the boundaries for interaction with
14500  * other DragDrop objects.</li>
14501  * <li>handle element(s): The drag operation only occurs if the element that
14502  * was clicked matches a handle element.  By default this is the linked
14503  * element, but there are times that you will want only a portion of the
14504  * linked element to initiate the drag operation, and the setHandleElId()
14505  * method provides a way to define this.</li>
14506  * <li>drag element: this represents the element that would be moved along
14507  * with the cursor during a drag operation.  By default, this is the linked
14508  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14509  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14510  * </li>
14511  * </ul>
14512  * This class should not be instantiated until the onload event to ensure that
14513  * the associated elements are available.
14514  * The following would define a DragDrop obj that would interact with any
14515  * other DragDrop obj in the "group1" group:
14516  * <pre>
14517  *  dd = new Roo.dd.DragDrop("div1", "group1");
14518  * </pre>
14519  * Since none of the event handlers have been implemented, nothing would
14520  * actually happen if you were to run the code above.  Normally you would
14521  * override this class or one of the default implementations, but you can
14522  * also override the methods you want on an instance of the class...
14523  * <pre>
14524  *  dd.onDragDrop = function(e, id) {
14525  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14526  *  }
14527  * </pre>
14528  * @constructor
14529  * @param {String} id of the element that is linked to this instance
14530  * @param {String} sGroup the group of related DragDrop objects
14531  * @param {object} config an object containing configurable attributes
14532  *                Valid properties for DragDrop:
14533  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14534  */
14535 Roo.dd.DragDrop = function(id, sGroup, config) {
14536     if (id) {
14537         this.init(id, sGroup, config);
14538     }
14539     
14540 };
14541
14542 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14543
14544     /**
14545      * The id of the element associated with this object.  This is what we
14546      * refer to as the "linked element" because the size and position of
14547      * this element is used to determine when the drag and drop objects have
14548      * interacted.
14549      * @property id
14550      * @type String
14551      */
14552     id: null,
14553
14554     /**
14555      * Configuration attributes passed into the constructor
14556      * @property config
14557      * @type object
14558      */
14559     config: null,
14560
14561     /**
14562      * The id of the element that will be dragged.  By default this is same
14563      * as the linked element , but could be changed to another element. Ex:
14564      * Roo.dd.DDProxy
14565      * @property dragElId
14566      * @type String
14567      * @private
14568      */
14569     dragElId: null,
14570
14571     /**
14572      * the id of the element that initiates the drag operation.  By default
14573      * this is the linked element, but could be changed to be a child of this
14574      * element.  This lets us do things like only starting the drag when the
14575      * header element within the linked html element is clicked.
14576      * @property handleElId
14577      * @type String
14578      * @private
14579      */
14580     handleElId: null,
14581
14582     /**
14583      * An associative array of HTML tags that will be ignored if clicked.
14584      * @property invalidHandleTypes
14585      * @type {string: string}
14586      */
14587     invalidHandleTypes: null,
14588
14589     /**
14590      * An associative array of ids for elements that will be ignored if clicked
14591      * @property invalidHandleIds
14592      * @type {string: string}
14593      */
14594     invalidHandleIds: null,
14595
14596     /**
14597      * An indexted array of css class names for elements that will be ignored
14598      * if clicked.
14599      * @property invalidHandleClasses
14600      * @type string[]
14601      */
14602     invalidHandleClasses: null,
14603
14604     /**
14605      * The linked element's absolute X position at the time the drag was
14606      * started
14607      * @property startPageX
14608      * @type int
14609      * @private
14610      */
14611     startPageX: 0,
14612
14613     /**
14614      * The linked element's absolute X position at the time the drag was
14615      * started
14616      * @property startPageY
14617      * @type int
14618      * @private
14619      */
14620     startPageY: 0,
14621
14622     /**
14623      * The group defines a logical collection of DragDrop objects that are
14624      * related.  Instances only get events when interacting with other
14625      * DragDrop object in the same group.  This lets us define multiple
14626      * groups using a single DragDrop subclass if we want.
14627      * @property groups
14628      * @type {string: string}
14629      */
14630     groups: null,
14631
14632     /**
14633      * Individual drag/drop instances can be locked.  This will prevent
14634      * onmousedown start drag.
14635      * @property locked
14636      * @type boolean
14637      * @private
14638      */
14639     locked: false,
14640
14641     /**
14642      * Lock this instance
14643      * @method lock
14644      */
14645     lock: function() { this.locked = true; },
14646
14647     /**
14648      * Unlock this instace
14649      * @method unlock
14650      */
14651     unlock: function() { this.locked = false; },
14652
14653     /**
14654      * By default, all insances can be a drop target.  This can be disabled by
14655      * setting isTarget to false.
14656      * @method isTarget
14657      * @type boolean
14658      */
14659     isTarget: true,
14660
14661     /**
14662      * The padding configured for this drag and drop object for calculating
14663      * the drop zone intersection with this object.
14664      * @method padding
14665      * @type int[]
14666      */
14667     padding: null,
14668
14669     /**
14670      * Cached reference to the linked element
14671      * @property _domRef
14672      * @private
14673      */
14674     _domRef: null,
14675
14676     /**
14677      * Internal typeof flag
14678      * @property __ygDragDrop
14679      * @private
14680      */
14681     __ygDragDrop: true,
14682
14683     /**
14684      * Set to true when horizontal contraints are applied
14685      * @property constrainX
14686      * @type boolean
14687      * @private
14688      */
14689     constrainX: false,
14690
14691     /**
14692      * Set to true when vertical contraints are applied
14693      * @property constrainY
14694      * @type boolean
14695      * @private
14696      */
14697     constrainY: false,
14698
14699     /**
14700      * The left constraint
14701      * @property minX
14702      * @type int
14703      * @private
14704      */
14705     minX: 0,
14706
14707     /**
14708      * The right constraint
14709      * @property maxX
14710      * @type int
14711      * @private
14712      */
14713     maxX: 0,
14714
14715     /**
14716      * The up constraint
14717      * @property minY
14718      * @type int
14719      * @type int
14720      * @private
14721      */
14722     minY: 0,
14723
14724     /**
14725      * The down constraint
14726      * @property maxY
14727      * @type int
14728      * @private
14729      */
14730     maxY: 0,
14731
14732     /**
14733      * Maintain offsets when we resetconstraints.  Set to true when you want
14734      * the position of the element relative to its parent to stay the same
14735      * when the page changes
14736      *
14737      * @property maintainOffset
14738      * @type boolean
14739      */
14740     maintainOffset: false,
14741
14742     /**
14743      * Array of pixel locations the element will snap to if we specified a
14744      * horizontal graduation/interval.  This array is generated automatically
14745      * when you define a tick interval.
14746      * @property xTicks
14747      * @type int[]
14748      */
14749     xTicks: null,
14750
14751     /**
14752      * Array of pixel locations the element will snap to if we specified a
14753      * vertical graduation/interval.  This array is generated automatically
14754      * when you define a tick interval.
14755      * @property yTicks
14756      * @type int[]
14757      */
14758     yTicks: null,
14759
14760     /**
14761      * By default the drag and drop instance will only respond to the primary
14762      * button click (left button for a right-handed mouse).  Set to true to
14763      * allow drag and drop to start with any mouse click that is propogated
14764      * by the browser
14765      * @property primaryButtonOnly
14766      * @type boolean
14767      */
14768     primaryButtonOnly: true,
14769
14770     /**
14771      * The availabe property is false until the linked dom element is accessible.
14772      * @property available
14773      * @type boolean
14774      */
14775     available: false,
14776
14777     /**
14778      * By default, drags can only be initiated if the mousedown occurs in the
14779      * region the linked element is.  This is done in part to work around a
14780      * bug in some browsers that mis-report the mousedown if the previous
14781      * mouseup happened outside of the window.  This property is set to true
14782      * if outer handles are defined.
14783      *
14784      * @property hasOuterHandles
14785      * @type boolean
14786      * @default false
14787      */
14788     hasOuterHandles: false,
14789
14790     /**
14791      * Code that executes immediately before the startDrag event
14792      * @method b4StartDrag
14793      * @private
14794      */
14795     b4StartDrag: function(x, y) { },
14796
14797     /**
14798      * Abstract method called after a drag/drop object is clicked
14799      * and the drag or mousedown time thresholds have beeen met.
14800      * @method startDrag
14801      * @param {int} X click location
14802      * @param {int} Y click location
14803      */
14804     startDrag: function(x, y) { /* override this */ },
14805
14806     /**
14807      * Code that executes immediately before the onDrag event
14808      * @method b4Drag
14809      * @private
14810      */
14811     b4Drag: function(e) { },
14812
14813     /**
14814      * Abstract method called during the onMouseMove event while dragging an
14815      * object.
14816      * @method onDrag
14817      * @param {Event} e the mousemove event
14818      */
14819     onDrag: function(e) { /* override this */ },
14820
14821     /**
14822      * Abstract method called when this element fist begins hovering over
14823      * another DragDrop obj
14824      * @method onDragEnter
14825      * @param {Event} e the mousemove event
14826      * @param {String|DragDrop[]} id In POINT mode, the element
14827      * id this is hovering over.  In INTERSECT mode, an array of one or more
14828      * dragdrop items being hovered over.
14829      */
14830     onDragEnter: function(e, id) { /* override this */ },
14831
14832     /**
14833      * Code that executes immediately before the onDragOver event
14834      * @method b4DragOver
14835      * @private
14836      */
14837     b4DragOver: function(e) { },
14838
14839     /**
14840      * Abstract method called when this element is hovering over another
14841      * DragDrop obj
14842      * @method onDragOver
14843      * @param {Event} e the mousemove event
14844      * @param {String|DragDrop[]} id In POINT mode, the element
14845      * id this is hovering over.  In INTERSECT mode, an array of dd items
14846      * being hovered over.
14847      */
14848     onDragOver: function(e, id) { /* override this */ },
14849
14850     /**
14851      * Code that executes immediately before the onDragOut event
14852      * @method b4DragOut
14853      * @private
14854      */
14855     b4DragOut: function(e) { },
14856
14857     /**
14858      * Abstract method called when we are no longer hovering over an element
14859      * @method onDragOut
14860      * @param {Event} e the mousemove event
14861      * @param {String|DragDrop[]} id In POINT mode, the element
14862      * id this was hovering over.  In INTERSECT mode, an array of dd items
14863      * that the mouse is no longer over.
14864      */
14865     onDragOut: function(e, id) { /* override this */ },
14866
14867     /**
14868      * Code that executes immediately before the onDragDrop event
14869      * @method b4DragDrop
14870      * @private
14871      */
14872     b4DragDrop: function(e) { },
14873
14874     /**
14875      * Abstract method called when this item is dropped on another DragDrop
14876      * obj
14877      * @method onDragDrop
14878      * @param {Event} e the mouseup event
14879      * @param {String|DragDrop[]} id In POINT mode, the element
14880      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14881      * was dropped on.
14882      */
14883     onDragDrop: function(e, id) { /* override this */ },
14884
14885     /**
14886      * Abstract method called when this item is dropped on an area with no
14887      * drop target
14888      * @method onInvalidDrop
14889      * @param {Event} e the mouseup event
14890      */
14891     onInvalidDrop: function(e) { /* override this */ },
14892
14893     /**
14894      * Code that executes immediately before the endDrag event
14895      * @method b4EndDrag
14896      * @private
14897      */
14898     b4EndDrag: function(e) { },
14899
14900     /**
14901      * Fired when we are done dragging the object
14902      * @method endDrag
14903      * @param {Event} e the mouseup event
14904      */
14905     endDrag: function(e) { /* override this */ },
14906
14907     /**
14908      * Code executed immediately before the onMouseDown event
14909      * @method b4MouseDown
14910      * @param {Event} e the mousedown event
14911      * @private
14912      */
14913     b4MouseDown: function(e) {  },
14914
14915     /**
14916      * Event handler that fires when a drag/drop obj gets a mousedown
14917      * @method onMouseDown
14918      * @param {Event} e the mousedown event
14919      */
14920     onMouseDown: function(e) { /* override this */ },
14921
14922     /**
14923      * Event handler that fires when a drag/drop obj gets a mouseup
14924      * @method onMouseUp
14925      * @param {Event} e the mouseup event
14926      */
14927     onMouseUp: function(e) { /* override this */ },
14928
14929     /**
14930      * Override the onAvailable method to do what is needed after the initial
14931      * position was determined.
14932      * @method onAvailable
14933      */
14934     onAvailable: function () {
14935     },
14936
14937     /*
14938      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14939      * @type Object
14940      */
14941     defaultPadding : {left:0, right:0, top:0, bottom:0},
14942
14943     /*
14944      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14945  *
14946  * Usage:
14947  <pre><code>
14948  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14949                 { dragElId: "existingProxyDiv" });
14950  dd.startDrag = function(){
14951      this.constrainTo("parent-id");
14952  };
14953  </code></pre>
14954  * Or you can initalize it using the {@link Roo.Element} object:
14955  <pre><code>
14956  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14957      startDrag : function(){
14958          this.constrainTo("parent-id");
14959      }
14960  });
14961  </code></pre>
14962      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14963      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14964      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14965      * an object containing the sides to pad. For example: {right:10, bottom:10}
14966      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14967      */
14968     constrainTo : function(constrainTo, pad, inContent){
14969         if(typeof pad == "number"){
14970             pad = {left: pad, right:pad, top:pad, bottom:pad};
14971         }
14972         pad = pad || this.defaultPadding;
14973         var b = Roo.get(this.getEl()).getBox();
14974         var ce = Roo.get(constrainTo);
14975         var s = ce.getScroll();
14976         var c, cd = ce.dom;
14977         if(cd == document.body){
14978             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14979         }else{
14980             xy = ce.getXY();
14981             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14982         }
14983
14984
14985         var topSpace = b.y - c.y;
14986         var leftSpace = b.x - c.x;
14987
14988         this.resetConstraints();
14989         this.setXConstraint(leftSpace - (pad.left||0), // left
14990                 c.width - leftSpace - b.width - (pad.right||0) //right
14991         );
14992         this.setYConstraint(topSpace - (pad.top||0), //top
14993                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14994         );
14995     },
14996
14997     /**
14998      * Returns a reference to the linked element
14999      * @method getEl
15000      * @return {HTMLElement} the html element
15001      */
15002     getEl: function() {
15003         if (!this._domRef) {
15004             this._domRef = Roo.getDom(this.id);
15005         }
15006
15007         return this._domRef;
15008     },
15009
15010     /**
15011      * Returns a reference to the actual element to drag.  By default this is
15012      * the same as the html element, but it can be assigned to another
15013      * element. An example of this can be found in Roo.dd.DDProxy
15014      * @method getDragEl
15015      * @return {HTMLElement} the html element
15016      */
15017     getDragEl: function() {
15018         return Roo.getDom(this.dragElId);
15019     },
15020
15021     /**
15022      * Sets up the DragDrop object.  Must be called in the constructor of any
15023      * Roo.dd.DragDrop subclass
15024      * @method init
15025      * @param id the id of the linked element
15026      * @param {String} sGroup the group of related items
15027      * @param {object} config configuration attributes
15028      */
15029     init: function(id, sGroup, config) {
15030         this.initTarget(id, sGroup, config);
15031         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15032         // Event.on(this.id, "selectstart", Event.preventDefault);
15033     },
15034
15035     /**
15036      * Initializes Targeting functionality only... the object does not
15037      * get a mousedown handler.
15038      * @method initTarget
15039      * @param id the id of the linked element
15040      * @param {String} sGroup the group of related items
15041      * @param {object} config configuration attributes
15042      */
15043     initTarget: function(id, sGroup, config) {
15044
15045         // configuration attributes
15046         this.config = config || {};
15047
15048         // create a local reference to the drag and drop manager
15049         this.DDM = Roo.dd.DDM;
15050         // initialize the groups array
15051         this.groups = {};
15052
15053         // assume that we have an element reference instead of an id if the
15054         // parameter is not a string
15055         if (typeof id !== "string") {
15056             id = Roo.id(id);
15057         }
15058
15059         // set the id
15060         this.id = id;
15061
15062         // add to an interaction group
15063         this.addToGroup((sGroup) ? sGroup : "default");
15064
15065         // We don't want to register this as the handle with the manager
15066         // so we just set the id rather than calling the setter.
15067         this.handleElId = id;
15068
15069         // the linked element is the element that gets dragged by default
15070         this.setDragElId(id);
15071
15072         // by default, clicked anchors will not start drag operations.
15073         this.invalidHandleTypes = { A: "A" };
15074         this.invalidHandleIds = {};
15075         this.invalidHandleClasses = [];
15076
15077         this.applyConfig();
15078
15079         this.handleOnAvailable();
15080     },
15081
15082     /**
15083      * Applies the configuration parameters that were passed into the constructor.
15084      * This is supposed to happen at each level through the inheritance chain.  So
15085      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15086      * DragDrop in order to get all of the parameters that are available in
15087      * each object.
15088      * @method applyConfig
15089      */
15090     applyConfig: function() {
15091
15092         // configurable properties:
15093         //    padding, isTarget, maintainOffset, primaryButtonOnly
15094         this.padding           = this.config.padding || [0, 0, 0, 0];
15095         this.isTarget          = (this.config.isTarget !== false);
15096         this.maintainOffset    = (this.config.maintainOffset);
15097         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15098
15099     },
15100
15101     /**
15102      * Executed when the linked element is available
15103      * @method handleOnAvailable
15104      * @private
15105      */
15106     handleOnAvailable: function() {
15107         this.available = true;
15108         this.resetConstraints();
15109         this.onAvailable();
15110     },
15111
15112      /**
15113      * Configures the padding for the target zone in px.  Effectively expands
15114      * (or reduces) the virtual object size for targeting calculations.
15115      * Supports css-style shorthand; if only one parameter is passed, all sides
15116      * will have that padding, and if only two are passed, the top and bottom
15117      * will have the first param, the left and right the second.
15118      * @method setPadding
15119      * @param {int} iTop    Top pad
15120      * @param {int} iRight  Right pad
15121      * @param {int} iBot    Bot pad
15122      * @param {int} iLeft   Left pad
15123      */
15124     setPadding: function(iTop, iRight, iBot, iLeft) {
15125         // this.padding = [iLeft, iRight, iTop, iBot];
15126         if (!iRight && 0 !== iRight) {
15127             this.padding = [iTop, iTop, iTop, iTop];
15128         } else if (!iBot && 0 !== iBot) {
15129             this.padding = [iTop, iRight, iTop, iRight];
15130         } else {
15131             this.padding = [iTop, iRight, iBot, iLeft];
15132         }
15133     },
15134
15135     /**
15136      * Stores the initial placement of the linked element.
15137      * @method setInitialPosition
15138      * @param {int} diffX   the X offset, default 0
15139      * @param {int} diffY   the Y offset, default 0
15140      */
15141     setInitPosition: function(diffX, diffY) {
15142         var el = this.getEl();
15143
15144         if (!this.DDM.verifyEl(el)) {
15145             return;
15146         }
15147
15148         var dx = diffX || 0;
15149         var dy = diffY || 0;
15150
15151         var p = Dom.getXY( el );
15152
15153         this.initPageX = p[0] - dx;
15154         this.initPageY = p[1] - dy;
15155
15156         this.lastPageX = p[0];
15157         this.lastPageY = p[1];
15158
15159
15160         this.setStartPosition(p);
15161     },
15162
15163     /**
15164      * Sets the start position of the element.  This is set when the obj
15165      * is initialized, the reset when a drag is started.
15166      * @method setStartPosition
15167      * @param pos current position (from previous lookup)
15168      * @private
15169      */
15170     setStartPosition: function(pos) {
15171         var p = pos || Dom.getXY( this.getEl() );
15172         this.deltaSetXY = null;
15173
15174         this.startPageX = p[0];
15175         this.startPageY = p[1];
15176     },
15177
15178     /**
15179      * Add this instance to a group of related drag/drop objects.  All
15180      * instances belong to at least one group, and can belong to as many
15181      * groups as needed.
15182      * @method addToGroup
15183      * @param sGroup {string} the name of the group
15184      */
15185     addToGroup: function(sGroup) {
15186         this.groups[sGroup] = true;
15187         this.DDM.regDragDrop(this, sGroup);
15188     },
15189
15190     /**
15191      * Remove's this instance from the supplied interaction group
15192      * @method removeFromGroup
15193      * @param {string}  sGroup  The group to drop
15194      */
15195     removeFromGroup: function(sGroup) {
15196         if (this.groups[sGroup]) {
15197             delete this.groups[sGroup];
15198         }
15199
15200         this.DDM.removeDDFromGroup(this, sGroup);
15201     },
15202
15203     /**
15204      * Allows you to specify that an element other than the linked element
15205      * will be moved with the cursor during a drag
15206      * @method setDragElId
15207      * @param id {string} the id of the element that will be used to initiate the drag
15208      */
15209     setDragElId: function(id) {
15210         this.dragElId = id;
15211     },
15212
15213     /**
15214      * Allows you to specify a child of the linked element that should be
15215      * used to initiate the drag operation.  An example of this would be if
15216      * you have a content div with text and links.  Clicking anywhere in the
15217      * content area would normally start the drag operation.  Use this method
15218      * to specify that an element inside of the content div is the element
15219      * that starts the drag operation.
15220      * @method setHandleElId
15221      * @param id {string} the id of the element that will be used to
15222      * initiate the drag.
15223      */
15224     setHandleElId: function(id) {
15225         if (typeof id !== "string") {
15226             id = Roo.id(id);
15227         }
15228         this.handleElId = id;
15229         this.DDM.regHandle(this.id, id);
15230     },
15231
15232     /**
15233      * Allows you to set an element outside of the linked element as a drag
15234      * handle
15235      * @method setOuterHandleElId
15236      * @param id the id of the element that will be used to initiate the drag
15237      */
15238     setOuterHandleElId: function(id) {
15239         if (typeof id !== "string") {
15240             id = Roo.id(id);
15241         }
15242         Event.on(id, "mousedown",
15243                 this.handleMouseDown, this);
15244         this.setHandleElId(id);
15245
15246         this.hasOuterHandles = true;
15247     },
15248
15249     /**
15250      * Remove all drag and drop hooks for this element
15251      * @method unreg
15252      */
15253     unreg: function() {
15254         Event.un(this.id, "mousedown",
15255                 this.handleMouseDown);
15256         this._domRef = null;
15257         this.DDM._remove(this);
15258     },
15259
15260     destroy : function(){
15261         this.unreg();
15262     },
15263
15264     /**
15265      * Returns true if this instance is locked, or the drag drop mgr is locked
15266      * (meaning that all drag/drop is disabled on the page.)
15267      * @method isLocked
15268      * @return {boolean} true if this obj or all drag/drop is locked, else
15269      * false
15270      */
15271     isLocked: function() {
15272         return (this.DDM.isLocked() || this.locked);
15273     },
15274
15275     /**
15276      * Fired when this object is clicked
15277      * @method handleMouseDown
15278      * @param {Event} e
15279      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15280      * @private
15281      */
15282     handleMouseDown: function(e, oDD){
15283         if (this.primaryButtonOnly && e.button != 0) {
15284             return;
15285         }
15286
15287         if (this.isLocked()) {
15288             return;
15289         }
15290
15291         this.DDM.refreshCache(this.groups);
15292
15293         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15294         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15295         } else {
15296             if (this.clickValidator(e)) {
15297
15298                 // set the initial element position
15299                 this.setStartPosition();
15300
15301
15302                 this.b4MouseDown(e);
15303                 this.onMouseDown(e);
15304
15305                 this.DDM.handleMouseDown(e, this);
15306
15307                 this.DDM.stopEvent(e);
15308             } else {
15309
15310
15311             }
15312         }
15313     },
15314
15315     clickValidator: function(e) {
15316         var target = e.getTarget();
15317         return ( this.isValidHandleChild(target) &&
15318                     (this.id == this.handleElId ||
15319                         this.DDM.handleWasClicked(target, this.id)) );
15320     },
15321
15322     /**
15323      * Allows you to specify a tag name that should not start a drag operation
15324      * when clicked.  This is designed to facilitate embedding links within a
15325      * drag handle that do something other than start the drag.
15326      * @method addInvalidHandleType
15327      * @param {string} tagName the type of element to exclude
15328      */
15329     addInvalidHandleType: function(tagName) {
15330         var type = tagName.toUpperCase();
15331         this.invalidHandleTypes[type] = type;
15332     },
15333
15334     /**
15335      * Lets you to specify an element id for a child of a drag handle
15336      * that should not initiate a drag
15337      * @method addInvalidHandleId
15338      * @param {string} id the element id of the element you wish to ignore
15339      */
15340     addInvalidHandleId: function(id) {
15341         if (typeof id !== "string") {
15342             id = Roo.id(id);
15343         }
15344         this.invalidHandleIds[id] = id;
15345     },
15346
15347     /**
15348      * Lets you specify a css class of elements that will not initiate a drag
15349      * @method addInvalidHandleClass
15350      * @param {string} cssClass the class of the elements you wish to ignore
15351      */
15352     addInvalidHandleClass: function(cssClass) {
15353         this.invalidHandleClasses.push(cssClass);
15354     },
15355
15356     /**
15357      * Unsets an excluded tag name set by addInvalidHandleType
15358      * @method removeInvalidHandleType
15359      * @param {string} tagName the type of element to unexclude
15360      */
15361     removeInvalidHandleType: function(tagName) {
15362         var type = tagName.toUpperCase();
15363         // this.invalidHandleTypes[type] = null;
15364         delete this.invalidHandleTypes[type];
15365     },
15366
15367     /**
15368      * Unsets an invalid handle id
15369      * @method removeInvalidHandleId
15370      * @param {string} id the id of the element to re-enable
15371      */
15372     removeInvalidHandleId: function(id) {
15373         if (typeof id !== "string") {
15374             id = Roo.id(id);
15375         }
15376         delete this.invalidHandleIds[id];
15377     },
15378
15379     /**
15380      * Unsets an invalid css class
15381      * @method removeInvalidHandleClass
15382      * @param {string} cssClass the class of the element(s) you wish to
15383      * re-enable
15384      */
15385     removeInvalidHandleClass: function(cssClass) {
15386         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15387             if (this.invalidHandleClasses[i] == cssClass) {
15388                 delete this.invalidHandleClasses[i];
15389             }
15390         }
15391     },
15392
15393     /**
15394      * Checks the tag exclusion list to see if this click should be ignored
15395      * @method isValidHandleChild
15396      * @param {HTMLElement} node the HTMLElement to evaluate
15397      * @return {boolean} true if this is a valid tag type, false if not
15398      */
15399     isValidHandleChild: function(node) {
15400
15401         var valid = true;
15402         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15403         var nodeName;
15404         try {
15405             nodeName = node.nodeName.toUpperCase();
15406         } catch(e) {
15407             nodeName = node.nodeName;
15408         }
15409         valid = valid && !this.invalidHandleTypes[nodeName];
15410         valid = valid && !this.invalidHandleIds[node.id];
15411
15412         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15413             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15414         }
15415
15416
15417         return valid;
15418
15419     },
15420
15421     /**
15422      * Create the array of horizontal tick marks if an interval was specified
15423      * in setXConstraint().
15424      * @method setXTicks
15425      * @private
15426      */
15427     setXTicks: function(iStartX, iTickSize) {
15428         this.xTicks = [];
15429         this.xTickSize = iTickSize;
15430
15431         var tickMap = {};
15432
15433         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15434             if (!tickMap[i]) {
15435                 this.xTicks[this.xTicks.length] = i;
15436                 tickMap[i] = true;
15437             }
15438         }
15439
15440         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15441             if (!tickMap[i]) {
15442                 this.xTicks[this.xTicks.length] = i;
15443                 tickMap[i] = true;
15444             }
15445         }
15446
15447         this.xTicks.sort(this.DDM.numericSort) ;
15448     },
15449
15450     /**
15451      * Create the array of vertical tick marks if an interval was specified in
15452      * setYConstraint().
15453      * @method setYTicks
15454      * @private
15455      */
15456     setYTicks: function(iStartY, iTickSize) {
15457         this.yTicks = [];
15458         this.yTickSize = iTickSize;
15459
15460         var tickMap = {};
15461
15462         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15463             if (!tickMap[i]) {
15464                 this.yTicks[this.yTicks.length] = i;
15465                 tickMap[i] = true;
15466             }
15467         }
15468
15469         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15470             if (!tickMap[i]) {
15471                 this.yTicks[this.yTicks.length] = i;
15472                 tickMap[i] = true;
15473             }
15474         }
15475
15476         this.yTicks.sort(this.DDM.numericSort) ;
15477     },
15478
15479     /**
15480      * By default, the element can be dragged any place on the screen.  Use
15481      * this method to limit the horizontal travel of the element.  Pass in
15482      * 0,0 for the parameters if you want to lock the drag to the y axis.
15483      * @method setXConstraint
15484      * @param {int} iLeft the number of pixels the element can move to the left
15485      * @param {int} iRight the number of pixels the element can move to the
15486      * right
15487      * @param {int} iTickSize optional parameter for specifying that the
15488      * element
15489      * should move iTickSize pixels at a time.
15490      */
15491     setXConstraint: function(iLeft, iRight, iTickSize) {
15492         this.leftConstraint = iLeft;
15493         this.rightConstraint = iRight;
15494
15495         this.minX = this.initPageX - iLeft;
15496         this.maxX = this.initPageX + iRight;
15497         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15498
15499         this.constrainX = true;
15500     },
15501
15502     /**
15503      * Clears any constraints applied to this instance.  Also clears ticks
15504      * since they can't exist independent of a constraint at this time.
15505      * @method clearConstraints
15506      */
15507     clearConstraints: function() {
15508         this.constrainX = false;
15509         this.constrainY = false;
15510         this.clearTicks();
15511     },
15512
15513     /**
15514      * Clears any tick interval defined for this instance
15515      * @method clearTicks
15516      */
15517     clearTicks: function() {
15518         this.xTicks = null;
15519         this.yTicks = null;
15520         this.xTickSize = 0;
15521         this.yTickSize = 0;
15522     },
15523
15524     /**
15525      * By default, the element can be dragged any place on the screen.  Set
15526      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15527      * parameters if you want to lock the drag to the x axis.
15528      * @method setYConstraint
15529      * @param {int} iUp the number of pixels the element can move up
15530      * @param {int} iDown the number of pixels the element can move down
15531      * @param {int} iTickSize optional parameter for specifying that the
15532      * element should move iTickSize pixels at a time.
15533      */
15534     setYConstraint: function(iUp, iDown, iTickSize) {
15535         this.topConstraint = iUp;
15536         this.bottomConstraint = iDown;
15537
15538         this.minY = this.initPageY - iUp;
15539         this.maxY = this.initPageY + iDown;
15540         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15541
15542         this.constrainY = true;
15543
15544     },
15545
15546     /**
15547      * resetConstraints must be called if you manually reposition a dd element.
15548      * @method resetConstraints
15549      * @param {boolean} maintainOffset
15550      */
15551     resetConstraints: function() {
15552
15553
15554         // Maintain offsets if necessary
15555         if (this.initPageX || this.initPageX === 0) {
15556             // figure out how much this thing has moved
15557             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15558             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15559
15560             this.setInitPosition(dx, dy);
15561
15562         // This is the first time we have detected the element's position
15563         } else {
15564             this.setInitPosition();
15565         }
15566
15567         if (this.constrainX) {
15568             this.setXConstraint( this.leftConstraint,
15569                                  this.rightConstraint,
15570                                  this.xTickSize        );
15571         }
15572
15573         if (this.constrainY) {
15574             this.setYConstraint( this.topConstraint,
15575                                  this.bottomConstraint,
15576                                  this.yTickSize         );
15577         }
15578     },
15579
15580     /**
15581      * Normally the drag element is moved pixel by pixel, but we can specify
15582      * that it move a number of pixels at a time.  This method resolves the
15583      * location when we have it set up like this.
15584      * @method getTick
15585      * @param {int} val where we want to place the object
15586      * @param {int[]} tickArray sorted array of valid points
15587      * @return {int} the closest tick
15588      * @private
15589      */
15590     getTick: function(val, tickArray) {
15591
15592         if (!tickArray) {
15593             // If tick interval is not defined, it is effectively 1 pixel,
15594             // so we return the value passed to us.
15595             return val;
15596         } else if (tickArray[0] >= val) {
15597             // The value is lower than the first tick, so we return the first
15598             // tick.
15599             return tickArray[0];
15600         } else {
15601             for (var i=0, len=tickArray.length; i<len; ++i) {
15602                 var next = i + 1;
15603                 if (tickArray[next] && tickArray[next] >= val) {
15604                     var diff1 = val - tickArray[i];
15605                     var diff2 = tickArray[next] - val;
15606                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15607                 }
15608             }
15609
15610             // The value is larger than the last tick, so we return the last
15611             // tick.
15612             return tickArray[tickArray.length - 1];
15613         }
15614     },
15615
15616     /**
15617      * toString method
15618      * @method toString
15619      * @return {string} string representation of the dd obj
15620      */
15621     toString: function() {
15622         return ("DragDrop " + this.id);
15623     }
15624
15625 });
15626
15627 })();
15628 /*
15629  * Based on:
15630  * Ext JS Library 1.1.1
15631  * Copyright(c) 2006-2007, Ext JS, LLC.
15632  *
15633  * Originally Released Under LGPL - original licence link has changed is not relivant.
15634  *
15635  * Fork - LGPL
15636  * <script type="text/javascript">
15637  */
15638
15639
15640 /**
15641  * The drag and drop utility provides a framework for building drag and drop
15642  * applications.  In addition to enabling drag and drop for specific elements,
15643  * the drag and drop elements are tracked by the manager class, and the
15644  * interactions between the various elements are tracked during the drag and
15645  * the implementing code is notified about these important moments.
15646  */
15647
15648 // Only load the library once.  Rewriting the manager class would orphan
15649 // existing drag and drop instances.
15650 if (!Roo.dd.DragDropMgr) {
15651
15652 /**
15653  * @class Roo.dd.DragDropMgr
15654  * DragDropMgr is a singleton that tracks the element interaction for
15655  * all DragDrop items in the window.  Generally, you will not call
15656  * this class directly, but it does have helper methods that could
15657  * be useful in your DragDrop implementations.
15658  * @singleton
15659  */
15660 Roo.dd.DragDropMgr = function() {
15661
15662     var Event = Roo.EventManager;
15663
15664     return {
15665
15666         /**
15667          * Two dimensional Array of registered DragDrop objects.  The first
15668          * dimension is the DragDrop item group, the second the DragDrop
15669          * object.
15670          * @property ids
15671          * @type {string: string}
15672          * @private
15673          * @static
15674          */
15675         ids: {},
15676
15677         /**
15678          * Array of element ids defined as drag handles.  Used to determine
15679          * if the element that generated the mousedown event is actually the
15680          * handle and not the html element itself.
15681          * @property handleIds
15682          * @type {string: string}
15683          * @private
15684          * @static
15685          */
15686         handleIds: {},
15687
15688         /**
15689          * the DragDrop object that is currently being dragged
15690          * @property dragCurrent
15691          * @type DragDrop
15692          * @private
15693          * @static
15694          **/
15695         dragCurrent: null,
15696
15697         /**
15698          * the DragDrop object(s) that are being hovered over
15699          * @property dragOvers
15700          * @type Array
15701          * @private
15702          * @static
15703          */
15704         dragOvers: {},
15705
15706         /**
15707          * the X distance between the cursor and the object being dragged
15708          * @property deltaX
15709          * @type int
15710          * @private
15711          * @static
15712          */
15713         deltaX: 0,
15714
15715         /**
15716          * the Y distance between the cursor and the object being dragged
15717          * @property deltaY
15718          * @type int
15719          * @private
15720          * @static
15721          */
15722         deltaY: 0,
15723
15724         /**
15725          * Flag to determine if we should prevent the default behavior of the
15726          * events we define. By default this is true, but this can be set to
15727          * false if you need the default behavior (not recommended)
15728          * @property preventDefault
15729          * @type boolean
15730          * @static
15731          */
15732         preventDefault: true,
15733
15734         /**
15735          * Flag to determine if we should stop the propagation of the events
15736          * we generate. This is true by default but you may want to set it to
15737          * false if the html element contains other features that require the
15738          * mouse click.
15739          * @property stopPropagation
15740          * @type boolean
15741          * @static
15742          */
15743         stopPropagation: true,
15744
15745         /**
15746          * Internal flag that is set to true when drag and drop has been
15747          * intialized
15748          * @property initialized
15749          * @private
15750          * @static
15751          */
15752         initalized: false,
15753
15754         /**
15755          * All drag and drop can be disabled.
15756          * @property locked
15757          * @private
15758          * @static
15759          */
15760         locked: false,
15761
15762         /**
15763          * Called the first time an element is registered.
15764          * @method init
15765          * @private
15766          * @static
15767          */
15768         init: function() {
15769             this.initialized = true;
15770         },
15771
15772         /**
15773          * In point mode, drag and drop interaction is defined by the
15774          * location of the cursor during the drag/drop
15775          * @property POINT
15776          * @type int
15777          * @static
15778          */
15779         POINT: 0,
15780
15781         /**
15782          * In intersect mode, drag and drop interactio nis defined by the
15783          * overlap of two or more drag and drop objects.
15784          * @property INTERSECT
15785          * @type int
15786          * @static
15787          */
15788         INTERSECT: 1,
15789
15790         /**
15791          * The current drag and drop mode.  Default: POINT
15792          * @property mode
15793          * @type int
15794          * @static
15795          */
15796         mode: 0,
15797
15798         /**
15799          * Runs method on all drag and drop objects
15800          * @method _execOnAll
15801          * @private
15802          * @static
15803          */
15804         _execOnAll: function(sMethod, args) {
15805             for (var i in this.ids) {
15806                 for (var j in this.ids[i]) {
15807                     var oDD = this.ids[i][j];
15808                     if (! this.isTypeOfDD(oDD)) {
15809                         continue;
15810                     }
15811                     oDD[sMethod].apply(oDD, args);
15812                 }
15813             }
15814         },
15815
15816         /**
15817          * Drag and drop initialization.  Sets up the global event handlers
15818          * @method _onLoad
15819          * @private
15820          * @static
15821          */
15822         _onLoad: function() {
15823
15824             this.init();
15825
15826
15827             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15828             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15829             Event.on(window,   "unload",    this._onUnload, this, true);
15830             Event.on(window,   "resize",    this._onResize, this, true);
15831             // Event.on(window,   "mouseout",    this._test);
15832
15833         },
15834
15835         /**
15836          * Reset constraints on all drag and drop objs
15837          * @method _onResize
15838          * @private
15839          * @static
15840          */
15841         _onResize: function(e) {
15842             this._execOnAll("resetConstraints", []);
15843         },
15844
15845         /**
15846          * Lock all drag and drop functionality
15847          * @method lock
15848          * @static
15849          */
15850         lock: function() { this.locked = true; },
15851
15852         /**
15853          * Unlock all drag and drop functionality
15854          * @method unlock
15855          * @static
15856          */
15857         unlock: function() { this.locked = false; },
15858
15859         /**
15860          * Is drag and drop locked?
15861          * @method isLocked
15862          * @return {boolean} True if drag and drop is locked, false otherwise.
15863          * @static
15864          */
15865         isLocked: function() { return this.locked; },
15866
15867         /**
15868          * Location cache that is set for all drag drop objects when a drag is
15869          * initiated, cleared when the drag is finished.
15870          * @property locationCache
15871          * @private
15872          * @static
15873          */
15874         locationCache: {},
15875
15876         /**
15877          * Set useCache to false if you want to force object the lookup of each
15878          * drag and drop linked element constantly during a drag.
15879          * @property useCache
15880          * @type boolean
15881          * @static
15882          */
15883         useCache: true,
15884
15885         /**
15886          * The number of pixels that the mouse needs to move after the
15887          * mousedown before the drag is initiated.  Default=3;
15888          * @property clickPixelThresh
15889          * @type int
15890          * @static
15891          */
15892         clickPixelThresh: 3,
15893
15894         /**
15895          * The number of milliseconds after the mousedown event to initiate the
15896          * drag if we don't get a mouseup event. Default=1000
15897          * @property clickTimeThresh
15898          * @type int
15899          * @static
15900          */
15901         clickTimeThresh: 350,
15902
15903         /**
15904          * Flag that indicates that either the drag pixel threshold or the
15905          * mousdown time threshold has been met
15906          * @property dragThreshMet
15907          * @type boolean
15908          * @private
15909          * @static
15910          */
15911         dragThreshMet: false,
15912
15913         /**
15914          * Timeout used for the click time threshold
15915          * @property clickTimeout
15916          * @type Object
15917          * @private
15918          * @static
15919          */
15920         clickTimeout: null,
15921
15922         /**
15923          * The X position of the mousedown event stored for later use when a
15924          * drag threshold is met.
15925          * @property startX
15926          * @type int
15927          * @private
15928          * @static
15929          */
15930         startX: 0,
15931
15932         /**
15933          * The Y position of the mousedown event stored for later use when a
15934          * drag threshold is met.
15935          * @property startY
15936          * @type int
15937          * @private
15938          * @static
15939          */
15940         startY: 0,
15941
15942         /**
15943          * Each DragDrop instance must be registered with the DragDropMgr.
15944          * This is executed in DragDrop.init()
15945          * @method regDragDrop
15946          * @param {DragDrop} oDD the DragDrop object to register
15947          * @param {String} sGroup the name of the group this element belongs to
15948          * @static
15949          */
15950         regDragDrop: function(oDD, sGroup) {
15951             if (!this.initialized) { this.init(); }
15952
15953             if (!this.ids[sGroup]) {
15954                 this.ids[sGroup] = {};
15955             }
15956             this.ids[sGroup][oDD.id] = oDD;
15957         },
15958
15959         /**
15960          * Removes the supplied dd instance from the supplied group. Executed
15961          * by DragDrop.removeFromGroup, so don't call this function directly.
15962          * @method removeDDFromGroup
15963          * @private
15964          * @static
15965          */
15966         removeDDFromGroup: function(oDD, sGroup) {
15967             if (!this.ids[sGroup]) {
15968                 this.ids[sGroup] = {};
15969             }
15970
15971             var obj = this.ids[sGroup];
15972             if (obj && obj[oDD.id]) {
15973                 delete obj[oDD.id];
15974             }
15975         },
15976
15977         /**
15978          * Unregisters a drag and drop item.  This is executed in
15979          * DragDrop.unreg, use that method instead of calling this directly.
15980          * @method _remove
15981          * @private
15982          * @static
15983          */
15984         _remove: function(oDD) {
15985             for (var g in oDD.groups) {
15986                 if (g && this.ids[g][oDD.id]) {
15987                     delete this.ids[g][oDD.id];
15988                 }
15989             }
15990             delete this.handleIds[oDD.id];
15991         },
15992
15993         /**
15994          * Each DragDrop handle element must be registered.  This is done
15995          * automatically when executing DragDrop.setHandleElId()
15996          * @method regHandle
15997          * @param {String} sDDId the DragDrop id this element is a handle for
15998          * @param {String} sHandleId the id of the element that is the drag
15999          * handle
16000          * @static
16001          */
16002         regHandle: function(sDDId, sHandleId) {
16003             if (!this.handleIds[sDDId]) {
16004                 this.handleIds[sDDId] = {};
16005             }
16006             this.handleIds[sDDId][sHandleId] = sHandleId;
16007         },
16008
16009         /**
16010          * Utility function to determine if a given element has been
16011          * registered as a drag drop item.
16012          * @method isDragDrop
16013          * @param {String} id the element id to check
16014          * @return {boolean} true if this element is a DragDrop item,
16015          * false otherwise
16016          * @static
16017          */
16018         isDragDrop: function(id) {
16019             return ( this.getDDById(id) ) ? true : false;
16020         },
16021
16022         /**
16023          * Returns the drag and drop instances that are in all groups the
16024          * passed in instance belongs to.
16025          * @method getRelated
16026          * @param {DragDrop} p_oDD the obj to get related data for
16027          * @param {boolean} bTargetsOnly if true, only return targetable objs
16028          * @return {DragDrop[]} the related instances
16029          * @static
16030          */
16031         getRelated: function(p_oDD, bTargetsOnly) {
16032             var oDDs = [];
16033             for (var i in p_oDD.groups) {
16034                 for (j in this.ids[i]) {
16035                     var dd = this.ids[i][j];
16036                     if (! this.isTypeOfDD(dd)) {
16037                         continue;
16038                     }
16039                     if (!bTargetsOnly || dd.isTarget) {
16040                         oDDs[oDDs.length] = dd;
16041                     }
16042                 }
16043             }
16044
16045             return oDDs;
16046         },
16047
16048         /**
16049          * Returns true if the specified dd target is a legal target for
16050          * the specifice drag obj
16051          * @method isLegalTarget
16052          * @param {DragDrop} the drag obj
16053          * @param {DragDrop} the target
16054          * @return {boolean} true if the target is a legal target for the
16055          * dd obj
16056          * @static
16057          */
16058         isLegalTarget: function (oDD, oTargetDD) {
16059             var targets = this.getRelated(oDD, true);
16060             for (var i=0, len=targets.length;i<len;++i) {
16061                 if (targets[i].id == oTargetDD.id) {
16062                     return true;
16063                 }
16064             }
16065
16066             return false;
16067         },
16068
16069         /**
16070          * My goal is to be able to transparently determine if an object is
16071          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16072          * returns "object", oDD.constructor.toString() always returns
16073          * "DragDrop" and not the name of the subclass.  So for now it just
16074          * evaluates a well-known variable in DragDrop.
16075          * @method isTypeOfDD
16076          * @param {Object} the object to evaluate
16077          * @return {boolean} true if typeof oDD = DragDrop
16078          * @static
16079          */
16080         isTypeOfDD: function (oDD) {
16081             return (oDD && oDD.__ygDragDrop);
16082         },
16083
16084         /**
16085          * Utility function to determine if a given element has been
16086          * registered as a drag drop handle for the given Drag Drop object.
16087          * @method isHandle
16088          * @param {String} id the element id to check
16089          * @return {boolean} true if this element is a DragDrop handle, false
16090          * otherwise
16091          * @static
16092          */
16093         isHandle: function(sDDId, sHandleId) {
16094             return ( this.handleIds[sDDId] &&
16095                             this.handleIds[sDDId][sHandleId] );
16096         },
16097
16098         /**
16099          * Returns the DragDrop instance for a given id
16100          * @method getDDById
16101          * @param {String} id the id of the DragDrop object
16102          * @return {DragDrop} the drag drop object, null if it is not found
16103          * @static
16104          */
16105         getDDById: function(id) {
16106             for (var i in this.ids) {
16107                 if (this.ids[i][id]) {
16108                     return this.ids[i][id];
16109                 }
16110             }
16111             return null;
16112         },
16113
16114         /**
16115          * Fired after a registered DragDrop object gets the mousedown event.
16116          * Sets up the events required to track the object being dragged
16117          * @method handleMouseDown
16118          * @param {Event} e the event
16119          * @param oDD the DragDrop object being dragged
16120          * @private
16121          * @static
16122          */
16123         handleMouseDown: function(e, oDD) {
16124             if(Roo.QuickTips){
16125                 Roo.QuickTips.disable();
16126             }
16127             this.currentTarget = e.getTarget();
16128
16129             this.dragCurrent = oDD;
16130
16131             var el = oDD.getEl();
16132
16133             // track start position
16134             this.startX = e.getPageX();
16135             this.startY = e.getPageY();
16136
16137             this.deltaX = this.startX - el.offsetLeft;
16138             this.deltaY = this.startY - el.offsetTop;
16139
16140             this.dragThreshMet = false;
16141
16142             this.clickTimeout = setTimeout(
16143                     function() {
16144                         var DDM = Roo.dd.DDM;
16145                         DDM.startDrag(DDM.startX, DDM.startY);
16146                     },
16147                     this.clickTimeThresh );
16148         },
16149
16150         /**
16151          * Fired when either the drag pixel threshol or the mousedown hold
16152          * time threshold has been met.
16153          * @method startDrag
16154          * @param x {int} the X position of the original mousedown
16155          * @param y {int} the Y position of the original mousedown
16156          * @static
16157          */
16158         startDrag: function(x, y) {
16159             clearTimeout(this.clickTimeout);
16160             if (this.dragCurrent) {
16161                 this.dragCurrent.b4StartDrag(x, y);
16162                 this.dragCurrent.startDrag(x, y);
16163             }
16164             this.dragThreshMet = true;
16165         },
16166
16167         /**
16168          * Internal function to handle the mouseup event.  Will be invoked
16169          * from the context of the document.
16170          * @method handleMouseUp
16171          * @param {Event} e the event
16172          * @private
16173          * @static
16174          */
16175         handleMouseUp: function(e) {
16176
16177             if(Roo.QuickTips){
16178                 Roo.QuickTips.enable();
16179             }
16180             if (! this.dragCurrent) {
16181                 return;
16182             }
16183
16184             clearTimeout(this.clickTimeout);
16185
16186             if (this.dragThreshMet) {
16187                 this.fireEvents(e, true);
16188             } else {
16189             }
16190
16191             this.stopDrag(e);
16192
16193             this.stopEvent(e);
16194         },
16195
16196         /**
16197          * Utility to stop event propagation and event default, if these
16198          * features are turned on.
16199          * @method stopEvent
16200          * @param {Event} e the event as returned by this.getEvent()
16201          * @static
16202          */
16203         stopEvent: function(e){
16204             if(this.stopPropagation) {
16205                 e.stopPropagation();
16206             }
16207
16208             if (this.preventDefault) {
16209                 e.preventDefault();
16210             }
16211         },
16212
16213         /**
16214          * Internal function to clean up event handlers after the drag
16215          * operation is complete
16216          * @method stopDrag
16217          * @param {Event} e the event
16218          * @private
16219          * @static
16220          */
16221         stopDrag: function(e) {
16222             // Fire the drag end event for the item that was dragged
16223             if (this.dragCurrent) {
16224                 if (this.dragThreshMet) {
16225                     this.dragCurrent.b4EndDrag(e);
16226                     this.dragCurrent.endDrag(e);
16227                 }
16228
16229                 this.dragCurrent.onMouseUp(e);
16230             }
16231
16232             this.dragCurrent = null;
16233             this.dragOvers = {};
16234         },
16235
16236         /**
16237          * Internal function to handle the mousemove event.  Will be invoked
16238          * from the context of the html element.
16239          *
16240          * @TODO figure out what we can do about mouse events lost when the
16241          * user drags objects beyond the window boundary.  Currently we can
16242          * detect this in internet explorer by verifying that the mouse is
16243          * down during the mousemove event.  Firefox doesn't give us the
16244          * button state on the mousemove event.
16245          * @method handleMouseMove
16246          * @param {Event} e the event
16247          * @private
16248          * @static
16249          */
16250         handleMouseMove: function(e) {
16251             if (! this.dragCurrent) {
16252                 return true;
16253             }
16254
16255             // var button = e.which || e.button;
16256
16257             // check for IE mouseup outside of page boundary
16258             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16259                 this.stopEvent(e);
16260                 return this.handleMouseUp(e);
16261             }
16262
16263             if (!this.dragThreshMet) {
16264                 var diffX = Math.abs(this.startX - e.getPageX());
16265                 var diffY = Math.abs(this.startY - e.getPageY());
16266                 if (diffX > this.clickPixelThresh ||
16267                             diffY > this.clickPixelThresh) {
16268                     this.startDrag(this.startX, this.startY);
16269                 }
16270             }
16271
16272             if (this.dragThreshMet) {
16273                 this.dragCurrent.b4Drag(e);
16274                 this.dragCurrent.onDrag(e);
16275                 if(!this.dragCurrent.moveOnly){
16276                     this.fireEvents(e, false);
16277                 }
16278             }
16279
16280             this.stopEvent(e);
16281
16282             return true;
16283         },
16284
16285         /**
16286          * Iterates over all of the DragDrop elements to find ones we are
16287          * hovering over or dropping on
16288          * @method fireEvents
16289          * @param {Event} e the event
16290          * @param {boolean} isDrop is this a drop op or a mouseover op?
16291          * @private
16292          * @static
16293          */
16294         fireEvents: function(e, isDrop) {
16295             var dc = this.dragCurrent;
16296
16297             // If the user did the mouse up outside of the window, we could
16298             // get here even though we have ended the drag.
16299             if (!dc || dc.isLocked()) {
16300                 return;
16301             }
16302
16303             var pt = e.getPoint();
16304
16305             // cache the previous dragOver array
16306             var oldOvers = [];
16307
16308             var outEvts   = [];
16309             var overEvts  = [];
16310             var dropEvts  = [];
16311             var enterEvts = [];
16312
16313             // Check to see if the object(s) we were hovering over is no longer
16314             // being hovered over so we can fire the onDragOut event
16315             for (var i in this.dragOvers) {
16316
16317                 var ddo = this.dragOvers[i];
16318
16319                 if (! this.isTypeOfDD(ddo)) {
16320                     continue;
16321                 }
16322
16323                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16324                     outEvts.push( ddo );
16325                 }
16326
16327                 oldOvers[i] = true;
16328                 delete this.dragOvers[i];
16329             }
16330
16331             for (var sGroup in dc.groups) {
16332
16333                 if ("string" != typeof sGroup) {
16334                     continue;
16335                 }
16336
16337                 for (i in this.ids[sGroup]) {
16338                     var oDD = this.ids[sGroup][i];
16339                     if (! this.isTypeOfDD(oDD)) {
16340                         continue;
16341                     }
16342
16343                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16344                         if (this.isOverTarget(pt, oDD, this.mode)) {
16345                             // look for drop interactions
16346                             if (isDrop) {
16347                                 dropEvts.push( oDD );
16348                             // look for drag enter and drag over interactions
16349                             } else {
16350
16351                                 // initial drag over: dragEnter fires
16352                                 if (!oldOvers[oDD.id]) {
16353                                     enterEvts.push( oDD );
16354                                 // subsequent drag overs: dragOver fires
16355                                 } else {
16356                                     overEvts.push( oDD );
16357                                 }
16358
16359                                 this.dragOvers[oDD.id] = oDD;
16360                             }
16361                         }
16362                     }
16363                 }
16364             }
16365
16366             if (this.mode) {
16367                 if (outEvts.length) {
16368                     dc.b4DragOut(e, outEvts);
16369                     dc.onDragOut(e, outEvts);
16370                 }
16371
16372                 if (enterEvts.length) {
16373                     dc.onDragEnter(e, enterEvts);
16374                 }
16375
16376                 if (overEvts.length) {
16377                     dc.b4DragOver(e, overEvts);
16378                     dc.onDragOver(e, overEvts);
16379                 }
16380
16381                 if (dropEvts.length) {
16382                     dc.b4DragDrop(e, dropEvts);
16383                     dc.onDragDrop(e, dropEvts);
16384                 }
16385
16386             } else {
16387                 // fire dragout events
16388                 var len = 0;
16389                 for (i=0, len=outEvts.length; i<len; ++i) {
16390                     dc.b4DragOut(e, outEvts[i].id);
16391                     dc.onDragOut(e, outEvts[i].id);
16392                 }
16393
16394                 // fire enter events
16395                 for (i=0,len=enterEvts.length; i<len; ++i) {
16396                     // dc.b4DragEnter(e, oDD.id);
16397                     dc.onDragEnter(e, enterEvts[i].id);
16398                 }
16399
16400                 // fire over events
16401                 for (i=0,len=overEvts.length; i<len; ++i) {
16402                     dc.b4DragOver(e, overEvts[i].id);
16403                     dc.onDragOver(e, overEvts[i].id);
16404                 }
16405
16406                 // fire drop events
16407                 for (i=0, len=dropEvts.length; i<len; ++i) {
16408                     dc.b4DragDrop(e, dropEvts[i].id);
16409                     dc.onDragDrop(e, dropEvts[i].id);
16410                 }
16411
16412             }
16413
16414             // notify about a drop that did not find a target
16415             if (isDrop && !dropEvts.length) {
16416                 dc.onInvalidDrop(e);
16417             }
16418
16419         },
16420
16421         /**
16422          * Helper function for getting the best match from the list of drag
16423          * and drop objects returned by the drag and drop events when we are
16424          * in INTERSECT mode.  It returns either the first object that the
16425          * cursor is over, or the object that has the greatest overlap with
16426          * the dragged element.
16427          * @method getBestMatch
16428          * @param  {DragDrop[]} dds The array of drag and drop objects
16429          * targeted
16430          * @return {DragDrop}       The best single match
16431          * @static
16432          */
16433         getBestMatch: function(dds) {
16434             var winner = null;
16435             // Return null if the input is not what we expect
16436             //if (!dds || !dds.length || dds.length == 0) {
16437                // winner = null;
16438             // If there is only one item, it wins
16439             //} else if (dds.length == 1) {
16440
16441             var len = dds.length;
16442
16443             if (len == 1) {
16444                 winner = dds[0];
16445             } else {
16446                 // Loop through the targeted items
16447                 for (var i=0; i<len; ++i) {
16448                     var dd = dds[i];
16449                     // If the cursor is over the object, it wins.  If the
16450                     // cursor is over multiple matches, the first one we come
16451                     // to wins.
16452                     if (dd.cursorIsOver) {
16453                         winner = dd;
16454                         break;
16455                     // Otherwise the object with the most overlap wins
16456                     } else {
16457                         if (!winner ||
16458                             winner.overlap.getArea() < dd.overlap.getArea()) {
16459                             winner = dd;
16460                         }
16461                     }
16462                 }
16463             }
16464
16465             return winner;
16466         },
16467
16468         /**
16469          * Refreshes the cache of the top-left and bottom-right points of the
16470          * drag and drop objects in the specified group(s).  This is in the
16471          * format that is stored in the drag and drop instance, so typical
16472          * usage is:
16473          * <code>
16474          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16475          * </code>
16476          * Alternatively:
16477          * <code>
16478          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16479          * </code>
16480          * @TODO this really should be an indexed array.  Alternatively this
16481          * method could accept both.
16482          * @method refreshCache
16483          * @param {Object} groups an associative array of groups to refresh
16484          * @static
16485          */
16486         refreshCache: function(groups) {
16487             for (var sGroup in groups) {
16488                 if ("string" != typeof sGroup) {
16489                     continue;
16490                 }
16491                 for (var i in this.ids[sGroup]) {
16492                     var oDD = this.ids[sGroup][i];
16493
16494                     if (this.isTypeOfDD(oDD)) {
16495                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16496                         var loc = this.getLocation(oDD);
16497                         if (loc) {
16498                             this.locationCache[oDD.id] = loc;
16499                         } else {
16500                             delete this.locationCache[oDD.id];
16501                             // this will unregister the drag and drop object if
16502                             // the element is not in a usable state
16503                             // oDD.unreg();
16504                         }
16505                     }
16506                 }
16507             }
16508         },
16509
16510         /**
16511          * This checks to make sure an element exists and is in the DOM.  The
16512          * main purpose is to handle cases where innerHTML is used to remove
16513          * drag and drop objects from the DOM.  IE provides an 'unspecified
16514          * error' when trying to access the offsetParent of such an element
16515          * @method verifyEl
16516          * @param {HTMLElement} el the element to check
16517          * @return {boolean} true if the element looks usable
16518          * @static
16519          */
16520         verifyEl: function(el) {
16521             if (el) {
16522                 var parent;
16523                 if(Roo.isIE){
16524                     try{
16525                         parent = el.offsetParent;
16526                     }catch(e){}
16527                 }else{
16528                     parent = el.offsetParent;
16529                 }
16530                 if (parent) {
16531                     return true;
16532                 }
16533             }
16534
16535             return false;
16536         },
16537
16538         /**
16539          * Returns a Region object containing the drag and drop element's position
16540          * and size, including the padding configured for it
16541          * @method getLocation
16542          * @param {DragDrop} oDD the drag and drop object to get the
16543          *                       location for
16544          * @return {Roo.lib.Region} a Region object representing the total area
16545          *                             the element occupies, including any padding
16546          *                             the instance is configured for.
16547          * @static
16548          */
16549         getLocation: function(oDD) {
16550             if (! this.isTypeOfDD(oDD)) {
16551                 return null;
16552             }
16553
16554             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16555
16556             try {
16557                 pos= Roo.lib.Dom.getXY(el);
16558             } catch (e) { }
16559
16560             if (!pos) {
16561                 return null;
16562             }
16563
16564             x1 = pos[0];
16565             x2 = x1 + el.offsetWidth;
16566             y1 = pos[1];
16567             y2 = y1 + el.offsetHeight;
16568
16569             t = y1 - oDD.padding[0];
16570             r = x2 + oDD.padding[1];
16571             b = y2 + oDD.padding[2];
16572             l = x1 - oDD.padding[3];
16573
16574             return new Roo.lib.Region( t, r, b, l );
16575         },
16576
16577         /**
16578          * Checks the cursor location to see if it over the target
16579          * @method isOverTarget
16580          * @param {Roo.lib.Point} pt The point to evaluate
16581          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16582          * @return {boolean} true if the mouse is over the target
16583          * @private
16584          * @static
16585          */
16586         isOverTarget: function(pt, oTarget, intersect) {
16587             // use cache if available
16588             var loc = this.locationCache[oTarget.id];
16589             if (!loc || !this.useCache) {
16590                 loc = this.getLocation(oTarget);
16591                 this.locationCache[oTarget.id] = loc;
16592
16593             }
16594
16595             if (!loc) {
16596                 return false;
16597             }
16598
16599             oTarget.cursorIsOver = loc.contains( pt );
16600
16601             // DragDrop is using this as a sanity check for the initial mousedown
16602             // in this case we are done.  In POINT mode, if the drag obj has no
16603             // contraints, we are also done. Otherwise we need to evaluate the
16604             // location of the target as related to the actual location of the
16605             // dragged element.
16606             var dc = this.dragCurrent;
16607             if (!dc || !dc.getTargetCoord ||
16608                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16609                 return oTarget.cursorIsOver;
16610             }
16611
16612             oTarget.overlap = null;
16613
16614             // Get the current location of the drag element, this is the
16615             // location of the mouse event less the delta that represents
16616             // where the original mousedown happened on the element.  We
16617             // need to consider constraints and ticks as well.
16618             var pos = dc.getTargetCoord(pt.x, pt.y);
16619
16620             var el = dc.getDragEl();
16621             var curRegion = new Roo.lib.Region( pos.y,
16622                                                    pos.x + el.offsetWidth,
16623                                                    pos.y + el.offsetHeight,
16624                                                    pos.x );
16625
16626             var overlap = curRegion.intersect(loc);
16627
16628             if (overlap) {
16629                 oTarget.overlap = overlap;
16630                 return (intersect) ? true : oTarget.cursorIsOver;
16631             } else {
16632                 return false;
16633             }
16634         },
16635
16636         /**
16637          * unload event handler
16638          * @method _onUnload
16639          * @private
16640          * @static
16641          */
16642         _onUnload: function(e, me) {
16643             Roo.dd.DragDropMgr.unregAll();
16644         },
16645
16646         /**
16647          * Cleans up the drag and drop events and objects.
16648          * @method unregAll
16649          * @private
16650          * @static
16651          */
16652         unregAll: function() {
16653
16654             if (this.dragCurrent) {
16655                 this.stopDrag();
16656                 this.dragCurrent = null;
16657             }
16658
16659             this._execOnAll("unreg", []);
16660
16661             for (i in this.elementCache) {
16662                 delete this.elementCache[i];
16663             }
16664
16665             this.elementCache = {};
16666             this.ids = {};
16667         },
16668
16669         /**
16670          * A cache of DOM elements
16671          * @property elementCache
16672          * @private
16673          * @static
16674          */
16675         elementCache: {},
16676
16677         /**
16678          * Get the wrapper for the DOM element specified
16679          * @method getElWrapper
16680          * @param {String} id the id of the element to get
16681          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16682          * @private
16683          * @deprecated This wrapper isn't that useful
16684          * @static
16685          */
16686         getElWrapper: function(id) {
16687             var oWrapper = this.elementCache[id];
16688             if (!oWrapper || !oWrapper.el) {
16689                 oWrapper = this.elementCache[id] =
16690                     new this.ElementWrapper(Roo.getDom(id));
16691             }
16692             return oWrapper;
16693         },
16694
16695         /**
16696          * Returns the actual DOM element
16697          * @method getElement
16698          * @param {String} id the id of the elment to get
16699          * @return {Object} The element
16700          * @deprecated use Roo.getDom instead
16701          * @static
16702          */
16703         getElement: function(id) {
16704             return Roo.getDom(id);
16705         },
16706
16707         /**
16708          * Returns the style property for the DOM element (i.e.,
16709          * document.getElById(id).style)
16710          * @method getCss
16711          * @param {String} id the id of the elment to get
16712          * @return {Object} The style property of the element
16713          * @deprecated use Roo.getDom instead
16714          * @static
16715          */
16716         getCss: function(id) {
16717             var el = Roo.getDom(id);
16718             return (el) ? el.style : null;
16719         },
16720
16721         /**
16722          * Inner class for cached elements
16723          * @class DragDropMgr.ElementWrapper
16724          * @for DragDropMgr
16725          * @private
16726          * @deprecated
16727          */
16728         ElementWrapper: function(el) {
16729                 /**
16730                  * The element
16731                  * @property el
16732                  */
16733                 this.el = el || null;
16734                 /**
16735                  * The element id
16736                  * @property id
16737                  */
16738                 this.id = this.el && el.id;
16739                 /**
16740                  * A reference to the style property
16741                  * @property css
16742                  */
16743                 this.css = this.el && el.style;
16744             },
16745
16746         /**
16747          * Returns the X position of an html element
16748          * @method getPosX
16749          * @param el the element for which to get the position
16750          * @return {int} the X coordinate
16751          * @for DragDropMgr
16752          * @deprecated use Roo.lib.Dom.getX instead
16753          * @static
16754          */
16755         getPosX: function(el) {
16756             return Roo.lib.Dom.getX(el);
16757         },
16758
16759         /**
16760          * Returns the Y position of an html element
16761          * @method getPosY
16762          * @param el the element for which to get the position
16763          * @return {int} the Y coordinate
16764          * @deprecated use Roo.lib.Dom.getY instead
16765          * @static
16766          */
16767         getPosY: function(el) {
16768             return Roo.lib.Dom.getY(el);
16769         },
16770
16771         /**
16772          * Swap two nodes.  In IE, we use the native method, for others we
16773          * emulate the IE behavior
16774          * @method swapNode
16775          * @param n1 the first node to swap
16776          * @param n2 the other node to swap
16777          * @static
16778          */
16779         swapNode: function(n1, n2) {
16780             if (n1.swapNode) {
16781                 n1.swapNode(n2);
16782             } else {
16783                 var p = n2.parentNode;
16784                 var s = n2.nextSibling;
16785
16786                 if (s == n1) {
16787                     p.insertBefore(n1, n2);
16788                 } else if (n2 == n1.nextSibling) {
16789                     p.insertBefore(n2, n1);
16790                 } else {
16791                     n1.parentNode.replaceChild(n2, n1);
16792                     p.insertBefore(n1, s);
16793                 }
16794             }
16795         },
16796
16797         /**
16798          * Returns the current scroll position
16799          * @method getScroll
16800          * @private
16801          * @static
16802          */
16803         getScroll: function () {
16804             var t, l, dde=document.documentElement, db=document.body;
16805             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16806                 t = dde.scrollTop;
16807                 l = dde.scrollLeft;
16808             } else if (db) {
16809                 t = db.scrollTop;
16810                 l = db.scrollLeft;
16811             } else {
16812
16813             }
16814             return { top: t, left: l };
16815         },
16816
16817         /**
16818          * Returns the specified element style property
16819          * @method getStyle
16820          * @param {HTMLElement} el          the element
16821          * @param {string}      styleProp   the style property
16822          * @return {string} The value of the style property
16823          * @deprecated use Roo.lib.Dom.getStyle
16824          * @static
16825          */
16826         getStyle: function(el, styleProp) {
16827             return Roo.fly(el).getStyle(styleProp);
16828         },
16829
16830         /**
16831          * Gets the scrollTop
16832          * @method getScrollTop
16833          * @return {int} the document's scrollTop
16834          * @static
16835          */
16836         getScrollTop: function () { return this.getScroll().top; },
16837
16838         /**
16839          * Gets the scrollLeft
16840          * @method getScrollLeft
16841          * @return {int} the document's scrollTop
16842          * @static
16843          */
16844         getScrollLeft: function () { return this.getScroll().left; },
16845
16846         /**
16847          * Sets the x/y position of an element to the location of the
16848          * target element.
16849          * @method moveToEl
16850          * @param {HTMLElement} moveEl      The element to move
16851          * @param {HTMLElement} targetEl    The position reference element
16852          * @static
16853          */
16854         moveToEl: function (moveEl, targetEl) {
16855             var aCoord = Roo.lib.Dom.getXY(targetEl);
16856             Roo.lib.Dom.setXY(moveEl, aCoord);
16857         },
16858
16859         /**
16860          * Numeric array sort function
16861          * @method numericSort
16862          * @static
16863          */
16864         numericSort: function(a, b) { return (a - b); },
16865
16866         /**
16867          * Internal counter
16868          * @property _timeoutCount
16869          * @private
16870          * @static
16871          */
16872         _timeoutCount: 0,
16873
16874         /**
16875          * Trying to make the load order less important.  Without this we get
16876          * an error if this file is loaded before the Event Utility.
16877          * @method _addListeners
16878          * @private
16879          * @static
16880          */
16881         _addListeners: function() {
16882             var DDM = Roo.dd.DDM;
16883             if ( Roo.lib.Event && document ) {
16884                 DDM._onLoad();
16885             } else {
16886                 if (DDM._timeoutCount > 2000) {
16887                 } else {
16888                     setTimeout(DDM._addListeners, 10);
16889                     if (document && document.body) {
16890                         DDM._timeoutCount += 1;
16891                     }
16892                 }
16893             }
16894         },
16895
16896         /**
16897          * Recursively searches the immediate parent and all child nodes for
16898          * the handle element in order to determine wheter or not it was
16899          * clicked.
16900          * @method handleWasClicked
16901          * @param node the html element to inspect
16902          * @static
16903          */
16904         handleWasClicked: function(node, id) {
16905             if (this.isHandle(id, node.id)) {
16906                 return true;
16907             } else {
16908                 // check to see if this is a text node child of the one we want
16909                 var p = node.parentNode;
16910
16911                 while (p) {
16912                     if (this.isHandle(id, p.id)) {
16913                         return true;
16914                     } else {
16915                         p = p.parentNode;
16916                     }
16917                 }
16918             }
16919
16920             return false;
16921         }
16922
16923     };
16924
16925 }();
16926
16927 // shorter alias, save a few bytes
16928 Roo.dd.DDM = Roo.dd.DragDropMgr;
16929 Roo.dd.DDM._addListeners();
16930
16931 }/*
16932  * Based on:
16933  * Ext JS Library 1.1.1
16934  * Copyright(c) 2006-2007, Ext JS, LLC.
16935  *
16936  * Originally Released Under LGPL - original licence link has changed is not relivant.
16937  *
16938  * Fork - LGPL
16939  * <script type="text/javascript">
16940  */
16941
16942 /**
16943  * @class Roo.dd.DD
16944  * A DragDrop implementation where the linked element follows the
16945  * mouse cursor during a drag.
16946  * @extends Roo.dd.DragDrop
16947  * @constructor
16948  * @param {String} id the id of the linked element
16949  * @param {String} sGroup the group of related DragDrop items
16950  * @param {object} config an object containing configurable attributes
16951  *                Valid properties for DD:
16952  *                    scroll
16953  */
16954 Roo.dd.DD = function(id, sGroup, config) {
16955     if (id) {
16956         this.init(id, sGroup, config);
16957     }
16958 };
16959
16960 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16961
16962     /**
16963      * When set to true, the utility automatically tries to scroll the browser
16964      * window wehn a drag and drop element is dragged near the viewport boundary.
16965      * Defaults to true.
16966      * @property scroll
16967      * @type boolean
16968      */
16969     scroll: true,
16970
16971     /**
16972      * Sets the pointer offset to the distance between the linked element's top
16973      * left corner and the location the element was clicked
16974      * @method autoOffset
16975      * @param {int} iPageX the X coordinate of the click
16976      * @param {int} iPageY the Y coordinate of the click
16977      */
16978     autoOffset: function(iPageX, iPageY) {
16979         var x = iPageX - this.startPageX;
16980         var y = iPageY - this.startPageY;
16981         this.setDelta(x, y);
16982     },
16983
16984     /**
16985      * Sets the pointer offset.  You can call this directly to force the
16986      * offset to be in a particular location (e.g., pass in 0,0 to set it
16987      * to the center of the object)
16988      * @method setDelta
16989      * @param {int} iDeltaX the distance from the left
16990      * @param {int} iDeltaY the distance from the top
16991      */
16992     setDelta: function(iDeltaX, iDeltaY) {
16993         this.deltaX = iDeltaX;
16994         this.deltaY = iDeltaY;
16995     },
16996
16997     /**
16998      * Sets the drag element to the location of the mousedown or click event,
16999      * maintaining the cursor location relative to the location on the element
17000      * that was clicked.  Override this if you want to place the element in a
17001      * location other than where the cursor is.
17002      * @method setDragElPos
17003      * @param {int} iPageX the X coordinate of the mousedown or drag event
17004      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17005      */
17006     setDragElPos: function(iPageX, iPageY) {
17007         // the first time we do this, we are going to check to make sure
17008         // the element has css positioning
17009
17010         var el = this.getDragEl();
17011         this.alignElWithMouse(el, iPageX, iPageY);
17012     },
17013
17014     /**
17015      * Sets the element to the location of the mousedown or click event,
17016      * maintaining the cursor location relative to the location on the element
17017      * that was clicked.  Override this if you want to place the element in a
17018      * location other than where the cursor is.
17019      * @method alignElWithMouse
17020      * @param {HTMLElement} el the element to move
17021      * @param {int} iPageX the X coordinate of the mousedown or drag event
17022      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17023      */
17024     alignElWithMouse: function(el, iPageX, iPageY) {
17025         var oCoord = this.getTargetCoord(iPageX, iPageY);
17026         var fly = el.dom ? el : Roo.fly(el);
17027         if (!this.deltaSetXY) {
17028             var aCoord = [oCoord.x, oCoord.y];
17029             fly.setXY(aCoord);
17030             var newLeft = fly.getLeft(true);
17031             var newTop  = fly.getTop(true);
17032             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17033         } else {
17034             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17035         }
17036
17037         this.cachePosition(oCoord.x, oCoord.y);
17038         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17039         return oCoord;
17040     },
17041
17042     /**
17043      * Saves the most recent position so that we can reset the constraints and
17044      * tick marks on-demand.  We need to know this so that we can calculate the
17045      * number of pixels the element is offset from its original position.
17046      * @method cachePosition
17047      * @param iPageX the current x position (optional, this just makes it so we
17048      * don't have to look it up again)
17049      * @param iPageY the current y position (optional, this just makes it so we
17050      * don't have to look it up again)
17051      */
17052     cachePosition: function(iPageX, iPageY) {
17053         if (iPageX) {
17054             this.lastPageX = iPageX;
17055             this.lastPageY = iPageY;
17056         } else {
17057             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17058             this.lastPageX = aCoord[0];
17059             this.lastPageY = aCoord[1];
17060         }
17061     },
17062
17063     /**
17064      * Auto-scroll the window if the dragged object has been moved beyond the
17065      * visible window boundary.
17066      * @method autoScroll
17067      * @param {int} x the drag element's x position
17068      * @param {int} y the drag element's y position
17069      * @param {int} h the height of the drag element
17070      * @param {int} w the width of the drag element
17071      * @private
17072      */
17073     autoScroll: function(x, y, h, w) {
17074
17075         if (this.scroll) {
17076             // The client height
17077             var clientH = Roo.lib.Dom.getViewWidth();
17078
17079             // The client width
17080             var clientW = Roo.lib.Dom.getViewHeight();
17081
17082             // The amt scrolled down
17083             var st = this.DDM.getScrollTop();
17084
17085             // The amt scrolled right
17086             var sl = this.DDM.getScrollLeft();
17087
17088             // Location of the bottom of the element
17089             var bot = h + y;
17090
17091             // Location of the right of the element
17092             var right = w + x;
17093
17094             // The distance from the cursor to the bottom of the visible area,
17095             // adjusted so that we don't scroll if the cursor is beyond the
17096             // element drag constraints
17097             var toBot = (clientH + st - y - this.deltaY);
17098
17099             // The distance from the cursor to the right of the visible area
17100             var toRight = (clientW + sl - x - this.deltaX);
17101
17102
17103             // How close to the edge the cursor must be before we scroll
17104             // var thresh = (document.all) ? 100 : 40;
17105             var thresh = 40;
17106
17107             // How many pixels to scroll per autoscroll op.  This helps to reduce
17108             // clunky scrolling. IE is more sensitive about this ... it needs this
17109             // value to be higher.
17110             var scrAmt = (document.all) ? 80 : 30;
17111
17112             // Scroll down if we are near the bottom of the visible page and the
17113             // obj extends below the crease
17114             if ( bot > clientH && toBot < thresh ) {
17115                 window.scrollTo(sl, st + scrAmt);
17116             }
17117
17118             // Scroll up if the window is scrolled down and the top of the object
17119             // goes above the top border
17120             if ( y < st && st > 0 && y - st < thresh ) {
17121                 window.scrollTo(sl, st - scrAmt);
17122             }
17123
17124             // Scroll right if the obj is beyond the right border and the cursor is
17125             // near the border.
17126             if ( right > clientW && toRight < thresh ) {
17127                 window.scrollTo(sl + scrAmt, st);
17128             }
17129
17130             // Scroll left if the window has been scrolled to the right and the obj
17131             // extends past the left border
17132             if ( x < sl && sl > 0 && x - sl < thresh ) {
17133                 window.scrollTo(sl - scrAmt, st);
17134             }
17135         }
17136     },
17137
17138     /**
17139      * Finds the location the element should be placed if we want to move
17140      * it to where the mouse location less the click offset would place us.
17141      * @method getTargetCoord
17142      * @param {int} iPageX the X coordinate of the click
17143      * @param {int} iPageY the Y coordinate of the click
17144      * @return an object that contains the coordinates (Object.x and Object.y)
17145      * @private
17146      */
17147     getTargetCoord: function(iPageX, iPageY) {
17148
17149
17150         var x = iPageX - this.deltaX;
17151         var y = iPageY - this.deltaY;
17152
17153         if (this.constrainX) {
17154             if (x < this.minX) { x = this.minX; }
17155             if (x > this.maxX) { x = this.maxX; }
17156         }
17157
17158         if (this.constrainY) {
17159             if (y < this.minY) { y = this.minY; }
17160             if (y > this.maxY) { y = this.maxY; }
17161         }
17162
17163         x = this.getTick(x, this.xTicks);
17164         y = this.getTick(y, this.yTicks);
17165
17166
17167         return {x:x, y:y};
17168     },
17169
17170     /*
17171      * Sets up config options specific to this class. Overrides
17172      * Roo.dd.DragDrop, but all versions of this method through the
17173      * inheritance chain are called
17174      */
17175     applyConfig: function() {
17176         Roo.dd.DD.superclass.applyConfig.call(this);
17177         this.scroll = (this.config.scroll !== false);
17178     },
17179
17180     /*
17181      * Event that fires prior to the onMouseDown event.  Overrides
17182      * Roo.dd.DragDrop.
17183      */
17184     b4MouseDown: function(e) {
17185         // this.resetConstraints();
17186         this.autoOffset(e.getPageX(),
17187                             e.getPageY());
17188     },
17189
17190     /*
17191      * Event that fires prior to the onDrag event.  Overrides
17192      * Roo.dd.DragDrop.
17193      */
17194     b4Drag: function(e) {
17195         this.setDragElPos(e.getPageX(),
17196                             e.getPageY());
17197     },
17198
17199     toString: function() {
17200         return ("DD " + this.id);
17201     }
17202
17203     //////////////////////////////////////////////////////////////////////////
17204     // Debugging ygDragDrop events that can be overridden
17205     //////////////////////////////////////////////////////////////////////////
17206     /*
17207     startDrag: function(x, y) {
17208     },
17209
17210     onDrag: function(e) {
17211     },
17212
17213     onDragEnter: function(e, id) {
17214     },
17215
17216     onDragOver: function(e, id) {
17217     },
17218
17219     onDragOut: function(e, id) {
17220     },
17221
17222     onDragDrop: function(e, id) {
17223     },
17224
17225     endDrag: function(e) {
17226     }
17227
17228     */
17229
17230 });/*
17231  * Based on:
17232  * Ext JS Library 1.1.1
17233  * Copyright(c) 2006-2007, Ext JS, LLC.
17234  *
17235  * Originally Released Under LGPL - original licence link has changed is not relivant.
17236  *
17237  * Fork - LGPL
17238  * <script type="text/javascript">
17239  */
17240
17241 /**
17242  * @class Roo.dd.DDProxy
17243  * A DragDrop implementation that inserts an empty, bordered div into
17244  * the document that follows the cursor during drag operations.  At the time of
17245  * the click, the frame div is resized to the dimensions of the linked html
17246  * element, and moved to the exact location of the linked element.
17247  *
17248  * References to the "frame" element refer to the single proxy element that
17249  * was created to be dragged in place of all DDProxy elements on the
17250  * page.
17251  *
17252  * @extends Roo.dd.DD
17253  * @constructor
17254  * @param {String} id the id of the linked html element
17255  * @param {String} sGroup the group of related DragDrop objects
17256  * @param {object} config an object containing configurable attributes
17257  *                Valid properties for DDProxy in addition to those in DragDrop:
17258  *                   resizeFrame, centerFrame, dragElId
17259  */
17260 Roo.dd.DDProxy = function(id, sGroup, config) {
17261     if (id) {
17262         this.init(id, sGroup, config);
17263         this.initFrame();
17264     }
17265 };
17266
17267 /**
17268  * The default drag frame div id
17269  * @property Roo.dd.DDProxy.dragElId
17270  * @type String
17271  * @static
17272  */
17273 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17274
17275 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17276
17277     /**
17278      * By default we resize the drag frame to be the same size as the element
17279      * we want to drag (this is to get the frame effect).  We can turn it off
17280      * if we want a different behavior.
17281      * @property resizeFrame
17282      * @type boolean
17283      */
17284     resizeFrame: true,
17285
17286     /**
17287      * By default the frame is positioned exactly where the drag element is, so
17288      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17289      * you do not have constraints on the obj is to have the drag frame centered
17290      * around the cursor.  Set centerFrame to true for this effect.
17291      * @property centerFrame
17292      * @type boolean
17293      */
17294     centerFrame: false,
17295
17296     /**
17297      * Creates the proxy element if it does not yet exist
17298      * @method createFrame
17299      */
17300     createFrame: function() {
17301         var self = this;
17302         var body = document.body;
17303
17304         if (!body || !body.firstChild) {
17305             setTimeout( function() { self.createFrame(); }, 50 );
17306             return;
17307         }
17308
17309         var div = this.getDragEl();
17310
17311         if (!div) {
17312             div    = document.createElement("div");
17313             div.id = this.dragElId;
17314             var s  = div.style;
17315
17316             s.position   = "absolute";
17317             s.visibility = "hidden";
17318             s.cursor     = "move";
17319             s.border     = "2px solid #aaa";
17320             s.zIndex     = 999;
17321
17322             // appendChild can blow up IE if invoked prior to the window load event
17323             // while rendering a table.  It is possible there are other scenarios
17324             // that would cause this to happen as well.
17325             body.insertBefore(div, body.firstChild);
17326         }
17327     },
17328
17329     /**
17330      * Initialization for the drag frame element.  Must be called in the
17331      * constructor of all subclasses
17332      * @method initFrame
17333      */
17334     initFrame: function() {
17335         this.createFrame();
17336     },
17337
17338     applyConfig: function() {
17339         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17340
17341         this.resizeFrame = (this.config.resizeFrame !== false);
17342         this.centerFrame = (this.config.centerFrame);
17343         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17344     },
17345
17346     /**
17347      * Resizes the drag frame to the dimensions of the clicked object, positions
17348      * it over the object, and finally displays it
17349      * @method showFrame
17350      * @param {int} iPageX X click position
17351      * @param {int} iPageY Y click position
17352      * @private
17353      */
17354     showFrame: function(iPageX, iPageY) {
17355         var el = this.getEl();
17356         var dragEl = this.getDragEl();
17357         var s = dragEl.style;
17358
17359         this._resizeProxy();
17360
17361         if (this.centerFrame) {
17362             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17363                            Math.round(parseInt(s.height, 10)/2) );
17364         }
17365
17366         this.setDragElPos(iPageX, iPageY);
17367
17368         Roo.fly(dragEl).show();
17369     },
17370
17371     /**
17372      * The proxy is automatically resized to the dimensions of the linked
17373      * element when a drag is initiated, unless resizeFrame is set to false
17374      * @method _resizeProxy
17375      * @private
17376      */
17377     _resizeProxy: function() {
17378         if (this.resizeFrame) {
17379             var el = this.getEl();
17380             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17381         }
17382     },
17383
17384     // overrides Roo.dd.DragDrop
17385     b4MouseDown: function(e) {
17386         var x = e.getPageX();
17387         var y = e.getPageY();
17388         this.autoOffset(x, y);
17389         this.setDragElPos(x, y);
17390     },
17391
17392     // overrides Roo.dd.DragDrop
17393     b4StartDrag: function(x, y) {
17394         // show the drag frame
17395         this.showFrame(x, y);
17396     },
17397
17398     // overrides Roo.dd.DragDrop
17399     b4EndDrag: function(e) {
17400         Roo.fly(this.getDragEl()).hide();
17401     },
17402
17403     // overrides Roo.dd.DragDrop
17404     // By default we try to move the element to the last location of the frame.
17405     // This is so that the default behavior mirrors that of Roo.dd.DD.
17406     endDrag: function(e) {
17407
17408         var lel = this.getEl();
17409         var del = this.getDragEl();
17410
17411         // Show the drag frame briefly so we can get its position
17412         del.style.visibility = "";
17413
17414         this.beforeMove();
17415         // Hide the linked element before the move to get around a Safari
17416         // rendering bug.
17417         lel.style.visibility = "hidden";
17418         Roo.dd.DDM.moveToEl(lel, del);
17419         del.style.visibility = "hidden";
17420         lel.style.visibility = "";
17421
17422         this.afterDrag();
17423     },
17424
17425     beforeMove : function(){
17426
17427     },
17428
17429     afterDrag : function(){
17430
17431     },
17432
17433     toString: function() {
17434         return ("DDProxy " + this.id);
17435     }
17436
17437 });
17438 /*
17439  * Based on:
17440  * Ext JS Library 1.1.1
17441  * Copyright(c) 2006-2007, Ext JS, LLC.
17442  *
17443  * Originally Released Under LGPL - original licence link has changed is not relivant.
17444  *
17445  * Fork - LGPL
17446  * <script type="text/javascript">
17447  */
17448
17449  /**
17450  * @class Roo.dd.DDTarget
17451  * A DragDrop implementation that does not move, but can be a drop
17452  * target.  You would get the same result by simply omitting implementation
17453  * for the event callbacks, but this way we reduce the processing cost of the
17454  * event listener and the callbacks.
17455  * @extends Roo.dd.DragDrop
17456  * @constructor
17457  * @param {String} id the id of the element that is a drop target
17458  * @param {String} sGroup the group of related DragDrop objects
17459  * @param {object} config an object containing configurable attributes
17460  *                 Valid properties for DDTarget in addition to those in
17461  *                 DragDrop:
17462  *                    none
17463  */
17464 Roo.dd.DDTarget = function(id, sGroup, config) {
17465     if (id) {
17466         this.initTarget(id, sGroup, config);
17467     }
17468     if (config.listeners || config.events) { 
17469        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17470             listeners : config.listeners || {}, 
17471             events : config.events || {} 
17472         });    
17473     }
17474 };
17475
17476 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17477 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17478     toString: function() {
17479         return ("DDTarget " + this.id);
17480     }
17481 });
17482 /*
17483  * Based on:
17484  * Ext JS Library 1.1.1
17485  * Copyright(c) 2006-2007, Ext JS, LLC.
17486  *
17487  * Originally Released Under LGPL - original licence link has changed is not relivant.
17488  *
17489  * Fork - LGPL
17490  * <script type="text/javascript">
17491  */
17492  
17493
17494 /**
17495  * @class Roo.dd.ScrollManager
17496  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17497  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17498  * @singleton
17499  */
17500 Roo.dd.ScrollManager = function(){
17501     var ddm = Roo.dd.DragDropMgr;
17502     var els = {};
17503     var dragEl = null;
17504     var proc = {};
17505     
17506     var onStop = function(e){
17507         dragEl = null;
17508         clearProc();
17509     };
17510     
17511     var triggerRefresh = function(){
17512         if(ddm.dragCurrent){
17513              ddm.refreshCache(ddm.dragCurrent.groups);
17514         }
17515     };
17516     
17517     var doScroll = function(){
17518         if(ddm.dragCurrent){
17519             var dds = Roo.dd.ScrollManager;
17520             if(!dds.animate){
17521                 if(proc.el.scroll(proc.dir, dds.increment)){
17522                     triggerRefresh();
17523                 }
17524             }else{
17525                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17526             }
17527         }
17528     };
17529     
17530     var clearProc = function(){
17531         if(proc.id){
17532             clearInterval(proc.id);
17533         }
17534         proc.id = 0;
17535         proc.el = null;
17536         proc.dir = "";
17537     };
17538     
17539     var startProc = function(el, dir){
17540         clearProc();
17541         proc.el = el;
17542         proc.dir = dir;
17543         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17544     };
17545     
17546     var onFire = function(e, isDrop){
17547         if(isDrop || !ddm.dragCurrent){ return; }
17548         var dds = Roo.dd.ScrollManager;
17549         if(!dragEl || dragEl != ddm.dragCurrent){
17550             dragEl = ddm.dragCurrent;
17551             // refresh regions on drag start
17552             dds.refreshCache();
17553         }
17554         
17555         var xy = Roo.lib.Event.getXY(e);
17556         var pt = new Roo.lib.Point(xy[0], xy[1]);
17557         for(var id in els){
17558             var el = els[id], r = el._region;
17559             if(r && r.contains(pt) && el.isScrollable()){
17560                 if(r.bottom - pt.y <= dds.thresh){
17561                     if(proc.el != el){
17562                         startProc(el, "down");
17563                     }
17564                     return;
17565                 }else if(r.right - pt.x <= dds.thresh){
17566                     if(proc.el != el){
17567                         startProc(el, "left");
17568                     }
17569                     return;
17570                 }else if(pt.y - r.top <= dds.thresh){
17571                     if(proc.el != el){
17572                         startProc(el, "up");
17573                     }
17574                     return;
17575                 }else if(pt.x - r.left <= dds.thresh){
17576                     if(proc.el != el){
17577                         startProc(el, "right");
17578                     }
17579                     return;
17580                 }
17581             }
17582         }
17583         clearProc();
17584     };
17585     
17586     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17587     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17588     
17589     return {
17590         /**
17591          * Registers new overflow element(s) to auto scroll
17592          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17593          */
17594         register : function(el){
17595             if(el instanceof Array){
17596                 for(var i = 0, len = el.length; i < len; i++) {
17597                         this.register(el[i]);
17598                 }
17599             }else{
17600                 el = Roo.get(el);
17601                 els[el.id] = el;
17602             }
17603         },
17604         
17605         /**
17606          * Unregisters overflow element(s) so they are no longer scrolled
17607          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17608          */
17609         unregister : function(el){
17610             if(el instanceof Array){
17611                 for(var i = 0, len = el.length; i < len; i++) {
17612                         this.unregister(el[i]);
17613                 }
17614             }else{
17615                 el = Roo.get(el);
17616                 delete els[el.id];
17617             }
17618         },
17619         
17620         /**
17621          * The number of pixels from the edge of a container the pointer needs to be to 
17622          * trigger scrolling (defaults to 25)
17623          * @type Number
17624          */
17625         thresh : 25,
17626         
17627         /**
17628          * The number of pixels to scroll in each scroll increment (defaults to 50)
17629          * @type Number
17630          */
17631         increment : 100,
17632         
17633         /**
17634          * The frequency of scrolls in milliseconds (defaults to 500)
17635          * @type Number
17636          */
17637         frequency : 500,
17638         
17639         /**
17640          * True to animate the scroll (defaults to true)
17641          * @type Boolean
17642          */
17643         animate: true,
17644         
17645         /**
17646          * The animation duration in seconds - 
17647          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17648          * @type Number
17649          */
17650         animDuration: .4,
17651         
17652         /**
17653          * Manually trigger a cache refresh.
17654          */
17655         refreshCache : function(){
17656             for(var id in els){
17657                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17658                     els[id]._region = els[id].getRegion();
17659                 }
17660             }
17661         }
17662     };
17663 }();/*
17664  * Based on:
17665  * Ext JS Library 1.1.1
17666  * Copyright(c) 2006-2007, Ext JS, LLC.
17667  *
17668  * Originally Released Under LGPL - original licence link has changed is not relivant.
17669  *
17670  * Fork - LGPL
17671  * <script type="text/javascript">
17672  */
17673  
17674
17675 /**
17676  * @class Roo.dd.Registry
17677  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17678  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17679  * @singleton
17680  */
17681 Roo.dd.Registry = function(){
17682     var elements = {}; 
17683     var handles = {}; 
17684     var autoIdSeed = 0;
17685
17686     var getId = function(el, autogen){
17687         if(typeof el == "string"){
17688             return el;
17689         }
17690         var id = el.id;
17691         if(!id && autogen !== false){
17692             id = "roodd-" + (++autoIdSeed);
17693             el.id = id;
17694         }
17695         return id;
17696     };
17697     
17698     return {
17699     /**
17700      * Register a drag drop element
17701      * @param {String|HTMLElement} element The id or DOM node to register
17702      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17703      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17704      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17705      * populated in the data object (if applicable):
17706      * <pre>
17707 Value      Description<br />
17708 ---------  ------------------------------------------<br />
17709 handles    Array of DOM nodes that trigger dragging<br />
17710            for the element being registered<br />
17711 isHandle   True if the element passed in triggers<br />
17712            dragging itself, else false
17713 </pre>
17714      */
17715         register : function(el, data){
17716             data = data || {};
17717             if(typeof el == "string"){
17718                 el = document.getElementById(el);
17719             }
17720             data.ddel = el;
17721             elements[getId(el)] = data;
17722             if(data.isHandle !== false){
17723                 handles[data.ddel.id] = data;
17724             }
17725             if(data.handles){
17726                 var hs = data.handles;
17727                 for(var i = 0, len = hs.length; i < len; i++){
17728                         handles[getId(hs[i])] = data;
17729                 }
17730             }
17731         },
17732
17733     /**
17734      * Unregister a drag drop element
17735      * @param {String|HTMLElement}  element The id or DOM node to unregister
17736      */
17737         unregister : function(el){
17738             var id = getId(el, false);
17739             var data = elements[id];
17740             if(data){
17741                 delete elements[id];
17742                 if(data.handles){
17743                     var hs = data.handles;
17744                     for(var i = 0, len = hs.length; i < len; i++){
17745                         delete handles[getId(hs[i], false)];
17746                     }
17747                 }
17748             }
17749         },
17750
17751     /**
17752      * Returns the handle registered for a DOM Node by id
17753      * @param {String|HTMLElement} id The DOM node or id to look up
17754      * @return {Object} handle The custom handle data
17755      */
17756         getHandle : function(id){
17757             if(typeof id != "string"){ // must be element?
17758                 id = id.id;
17759             }
17760             return handles[id];
17761         },
17762
17763     /**
17764      * Returns the handle that is registered for the DOM node that is the target of the event
17765      * @param {Event} e The event
17766      * @return {Object} handle The custom handle data
17767      */
17768         getHandleFromEvent : function(e){
17769             var t = Roo.lib.Event.getTarget(e);
17770             return t ? handles[t.id] : null;
17771         },
17772
17773     /**
17774      * Returns a custom data object that is registered for a DOM node by id
17775      * @param {String|HTMLElement} id The DOM node or id to look up
17776      * @return {Object} data The custom data
17777      */
17778         getTarget : function(id){
17779             if(typeof id != "string"){ // must be element?
17780                 id = id.id;
17781             }
17782             return elements[id];
17783         },
17784
17785     /**
17786      * Returns a custom data object that is registered for the DOM node that is the target of the event
17787      * @param {Event} e The event
17788      * @return {Object} data The custom data
17789      */
17790         getTargetFromEvent : function(e){
17791             var t = Roo.lib.Event.getTarget(e);
17792             return t ? elements[t.id] || handles[t.id] : null;
17793         }
17794     };
17795 }();/*
17796  * Based on:
17797  * Ext JS Library 1.1.1
17798  * Copyright(c) 2006-2007, Ext JS, LLC.
17799  *
17800  * Originally Released Under LGPL - original licence link has changed is not relivant.
17801  *
17802  * Fork - LGPL
17803  * <script type="text/javascript">
17804  */
17805  
17806
17807 /**
17808  * @class Roo.dd.StatusProxy
17809  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17810  * default drag proxy used by all Roo.dd components.
17811  * @constructor
17812  * @param {Object} config
17813  */
17814 Roo.dd.StatusProxy = function(config){
17815     Roo.apply(this, config);
17816     this.id = this.id || Roo.id();
17817     this.el = new Roo.Layer({
17818         dh: {
17819             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17820                 {tag: "div", cls: "x-dd-drop-icon"},
17821                 {tag: "div", cls: "x-dd-drag-ghost"}
17822             ]
17823         }, 
17824         shadow: !config || config.shadow !== false
17825     });
17826     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17827     this.dropStatus = this.dropNotAllowed;
17828 };
17829
17830 Roo.dd.StatusProxy.prototype = {
17831     /**
17832      * @cfg {String} dropAllowed
17833      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17834      */
17835     dropAllowed : "x-dd-drop-ok",
17836     /**
17837      * @cfg {String} dropNotAllowed
17838      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17839      */
17840     dropNotAllowed : "x-dd-drop-nodrop",
17841
17842     /**
17843      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17844      * over the current target element.
17845      * @param {String} cssClass The css class for the new drop status indicator image
17846      */
17847     setStatus : function(cssClass){
17848         cssClass = cssClass || this.dropNotAllowed;
17849         if(this.dropStatus != cssClass){
17850             this.el.replaceClass(this.dropStatus, cssClass);
17851             this.dropStatus = cssClass;
17852         }
17853     },
17854
17855     /**
17856      * Resets the status indicator to the default dropNotAllowed value
17857      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17858      */
17859     reset : function(clearGhost){
17860         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17861         this.dropStatus = this.dropNotAllowed;
17862         if(clearGhost){
17863             this.ghost.update("");
17864         }
17865     },
17866
17867     /**
17868      * Updates the contents of the ghost element
17869      * @param {String} html The html that will replace the current innerHTML of the ghost element
17870      */
17871     update : function(html){
17872         if(typeof html == "string"){
17873             this.ghost.update(html);
17874         }else{
17875             this.ghost.update("");
17876             html.style.margin = "0";
17877             this.ghost.dom.appendChild(html);
17878         }
17879         // ensure float = none set?? cant remember why though.
17880         var el = this.ghost.dom.firstChild;
17881                 if(el){
17882                         Roo.fly(el).setStyle('float', 'none');
17883                 }
17884     },
17885     
17886     /**
17887      * Returns the underlying proxy {@link Roo.Layer}
17888      * @return {Roo.Layer} el
17889     */
17890     getEl : function(){
17891         return this.el;
17892     },
17893
17894     /**
17895      * Returns the ghost element
17896      * @return {Roo.Element} el
17897      */
17898     getGhost : function(){
17899         return this.ghost;
17900     },
17901
17902     /**
17903      * Hides the proxy
17904      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17905      */
17906     hide : function(clear){
17907         this.el.hide();
17908         if(clear){
17909             this.reset(true);
17910         }
17911     },
17912
17913     /**
17914      * Stops the repair animation if it's currently running
17915      */
17916     stop : function(){
17917         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17918             this.anim.stop();
17919         }
17920     },
17921
17922     /**
17923      * Displays this proxy
17924      */
17925     show : function(){
17926         this.el.show();
17927     },
17928
17929     /**
17930      * Force the Layer to sync its shadow and shim positions to the element
17931      */
17932     sync : function(){
17933         this.el.sync();
17934     },
17935
17936     /**
17937      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17938      * invalid drop operation by the item being dragged.
17939      * @param {Array} xy The XY position of the element ([x, y])
17940      * @param {Function} callback The function to call after the repair is complete
17941      * @param {Object} scope The scope in which to execute the callback
17942      */
17943     repair : function(xy, callback, scope){
17944         this.callback = callback;
17945         this.scope = scope;
17946         if(xy && this.animRepair !== false){
17947             this.el.addClass("x-dd-drag-repair");
17948             this.el.hideUnders(true);
17949             this.anim = this.el.shift({
17950                 duration: this.repairDuration || .5,
17951                 easing: 'easeOut',
17952                 xy: xy,
17953                 stopFx: true,
17954                 callback: this.afterRepair,
17955                 scope: this
17956             });
17957         }else{
17958             this.afterRepair();
17959         }
17960     },
17961
17962     // private
17963     afterRepair : function(){
17964         this.hide(true);
17965         if(typeof this.callback == "function"){
17966             this.callback.call(this.scope || this);
17967         }
17968         this.callback = null;
17969         this.scope = null;
17970     }
17971 };/*
17972  * Based on:
17973  * Ext JS Library 1.1.1
17974  * Copyright(c) 2006-2007, Ext JS, LLC.
17975  *
17976  * Originally Released Under LGPL - original licence link has changed is not relivant.
17977  *
17978  * Fork - LGPL
17979  * <script type="text/javascript">
17980  */
17981
17982 /**
17983  * @class Roo.dd.DragSource
17984  * @extends Roo.dd.DDProxy
17985  * A simple class that provides the basic implementation needed to make any element draggable.
17986  * @constructor
17987  * @param {String/HTMLElement/Element} el The container element
17988  * @param {Object} config
17989  */
17990 Roo.dd.DragSource = function(el, config){
17991     this.el = Roo.get(el);
17992     this.dragData = {};
17993     
17994     Roo.apply(this, config);
17995     
17996     if(!this.proxy){
17997         this.proxy = new Roo.dd.StatusProxy();
17998     }
17999
18000     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18001           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18002     
18003     this.dragging = false;
18004 };
18005
18006 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18007     /**
18008      * @cfg {String} dropAllowed
18009      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18010      */
18011     dropAllowed : "x-dd-drop-ok",
18012     /**
18013      * @cfg {String} dropNotAllowed
18014      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18015      */
18016     dropNotAllowed : "x-dd-drop-nodrop",
18017
18018     /**
18019      * Returns the data object associated with this drag source
18020      * @return {Object} data An object containing arbitrary data
18021      */
18022     getDragData : function(e){
18023         return this.dragData;
18024     },
18025
18026     // private
18027     onDragEnter : function(e, id){
18028         var target = Roo.dd.DragDropMgr.getDDById(id);
18029         this.cachedTarget = target;
18030         if(this.beforeDragEnter(target, e, id) !== false){
18031             if(target.isNotifyTarget){
18032                 var status = target.notifyEnter(this, e, this.dragData);
18033                 this.proxy.setStatus(status);
18034             }else{
18035                 this.proxy.setStatus(this.dropAllowed);
18036             }
18037             
18038             if(this.afterDragEnter){
18039                 /**
18040                  * An empty function by default, but provided so that you can perform a custom action
18041                  * when the dragged item enters the drop target by providing an implementation.
18042                  * @param {Roo.dd.DragDrop} target The drop target
18043                  * @param {Event} e The event object
18044                  * @param {String} id The id of the dragged element
18045                  * @method afterDragEnter
18046                  */
18047                 this.afterDragEnter(target, e, id);
18048             }
18049         }
18050     },
18051
18052     /**
18053      * An empty function by default, but provided so that you can perform a custom action
18054      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18055      * @param {Roo.dd.DragDrop} target The drop target
18056      * @param {Event} e The event object
18057      * @param {String} id The id of the dragged element
18058      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18059      */
18060     beforeDragEnter : function(target, e, id){
18061         return true;
18062     },
18063
18064     // private
18065     alignElWithMouse: function() {
18066         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18067         this.proxy.sync();
18068     },
18069
18070     // private
18071     onDragOver : function(e, id){
18072         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18073         if(this.beforeDragOver(target, e, id) !== false){
18074             if(target.isNotifyTarget){
18075                 var status = target.notifyOver(this, e, this.dragData);
18076                 this.proxy.setStatus(status);
18077             }
18078
18079             if(this.afterDragOver){
18080                 /**
18081                  * An empty function by default, but provided so that you can perform a custom action
18082                  * while the dragged item is over the drop target by providing an implementation.
18083                  * @param {Roo.dd.DragDrop} target The drop target
18084                  * @param {Event} e The event object
18085                  * @param {String} id The id of the dragged element
18086                  * @method afterDragOver
18087                  */
18088                 this.afterDragOver(target, e, id);
18089             }
18090         }
18091     },
18092
18093     /**
18094      * An empty function by default, but provided so that you can perform a custom action
18095      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18096      * @param {Roo.dd.DragDrop} target The drop target
18097      * @param {Event} e The event object
18098      * @param {String} id The id of the dragged element
18099      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18100      */
18101     beforeDragOver : function(target, e, id){
18102         return true;
18103     },
18104
18105     // private
18106     onDragOut : function(e, id){
18107         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18108         if(this.beforeDragOut(target, e, id) !== false){
18109             if(target.isNotifyTarget){
18110                 target.notifyOut(this, e, this.dragData);
18111             }
18112             this.proxy.reset();
18113             if(this.afterDragOut){
18114                 /**
18115                  * An empty function by default, but provided so that you can perform a custom action
18116                  * after the dragged item is dragged out of the target without dropping.
18117                  * @param {Roo.dd.DragDrop} target The drop target
18118                  * @param {Event} e The event object
18119                  * @param {String} id The id of the dragged element
18120                  * @method afterDragOut
18121                  */
18122                 this.afterDragOut(target, e, id);
18123             }
18124         }
18125         this.cachedTarget = null;
18126     },
18127
18128     /**
18129      * An empty function by default, but provided so that you can perform a custom action before the dragged
18130      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18131      * @param {Roo.dd.DragDrop} target The drop target
18132      * @param {Event} e The event object
18133      * @param {String} id The id of the dragged element
18134      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18135      */
18136     beforeDragOut : function(target, e, id){
18137         return true;
18138     },
18139     
18140     // private
18141     onDragDrop : function(e, id){
18142         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18143         if(this.beforeDragDrop(target, e, id) !== false){
18144             if(target.isNotifyTarget){
18145                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18146                     this.onValidDrop(target, e, id);
18147                 }else{
18148                     this.onInvalidDrop(target, e, id);
18149                 }
18150             }else{
18151                 this.onValidDrop(target, e, id);
18152             }
18153             
18154             if(this.afterDragDrop){
18155                 /**
18156                  * An empty function by default, but provided so that you can perform a custom action
18157                  * after a valid drag drop has occurred by providing an implementation.
18158                  * @param {Roo.dd.DragDrop} target The drop target
18159                  * @param {Event} e The event object
18160                  * @param {String} id The id of the dropped element
18161                  * @method afterDragDrop
18162                  */
18163                 this.afterDragDrop(target, e, id);
18164             }
18165         }
18166         delete this.cachedTarget;
18167     },
18168
18169     /**
18170      * An empty function by default, but provided so that you can perform a custom action before the dragged
18171      * item is dropped onto the target and optionally cancel the onDragDrop.
18172      * @param {Roo.dd.DragDrop} target The drop target
18173      * @param {Event} e The event object
18174      * @param {String} id The id of the dragged element
18175      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18176      */
18177     beforeDragDrop : function(target, e, id){
18178         return true;
18179     },
18180
18181     // private
18182     onValidDrop : function(target, e, id){
18183         this.hideProxy();
18184         if(this.afterValidDrop){
18185             /**
18186              * An empty function by default, but provided so that you can perform a custom action
18187              * after a valid drop has occurred by providing an implementation.
18188              * @param {Object} target The target DD 
18189              * @param {Event} e The event object
18190              * @param {String} id The id of the dropped element
18191              * @method afterInvalidDrop
18192              */
18193             this.afterValidDrop(target, e, id);
18194         }
18195     },
18196
18197     // private
18198     getRepairXY : function(e, data){
18199         return this.el.getXY();  
18200     },
18201
18202     // private
18203     onInvalidDrop : function(target, e, id){
18204         this.beforeInvalidDrop(target, e, id);
18205         if(this.cachedTarget){
18206             if(this.cachedTarget.isNotifyTarget){
18207                 this.cachedTarget.notifyOut(this, e, this.dragData);
18208             }
18209             this.cacheTarget = null;
18210         }
18211         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18212
18213         if(this.afterInvalidDrop){
18214             /**
18215              * An empty function by default, but provided so that you can perform a custom action
18216              * after an invalid drop has occurred by providing an implementation.
18217              * @param {Event} e The event object
18218              * @param {String} id The id of the dropped element
18219              * @method afterInvalidDrop
18220              */
18221             this.afterInvalidDrop(e, id);
18222         }
18223     },
18224
18225     // private
18226     afterRepair : function(){
18227         if(Roo.enableFx){
18228             this.el.highlight(this.hlColor || "c3daf9");
18229         }
18230         this.dragging = false;
18231     },
18232
18233     /**
18234      * An empty function by default, but provided so that you can perform a custom action after an invalid
18235      * drop has occurred.
18236      * @param {Roo.dd.DragDrop} target The drop target
18237      * @param {Event} e The event object
18238      * @param {String} id The id of the dragged element
18239      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18240      */
18241     beforeInvalidDrop : function(target, e, id){
18242         return true;
18243     },
18244
18245     // private
18246     handleMouseDown : function(e){
18247         if(this.dragging) {
18248             return;
18249         }
18250         var data = this.getDragData(e);
18251         if(data && this.onBeforeDrag(data, e) !== false){
18252             this.dragData = data;
18253             this.proxy.stop();
18254             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18255         } 
18256     },
18257
18258     /**
18259      * An empty function by default, but provided so that you can perform a custom action before the initial
18260      * drag event begins and optionally cancel it.
18261      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18262      * @param {Event} e The event object
18263      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18264      */
18265     onBeforeDrag : function(data, e){
18266         return true;
18267     },
18268
18269     /**
18270      * An empty function by default, but provided so that you can perform a custom action once the initial
18271      * drag event has begun.  The drag cannot be canceled from this function.
18272      * @param {Number} x The x position of the click on the dragged object
18273      * @param {Number} y The y position of the click on the dragged object
18274      */
18275     onStartDrag : Roo.emptyFn,
18276
18277     // private - YUI override
18278     startDrag : function(x, y){
18279         this.proxy.reset();
18280         this.dragging = true;
18281         this.proxy.update("");
18282         this.onInitDrag(x, y);
18283         this.proxy.show();
18284     },
18285
18286     // private
18287     onInitDrag : function(x, y){
18288         var clone = this.el.dom.cloneNode(true);
18289         clone.id = Roo.id(); // prevent duplicate ids
18290         this.proxy.update(clone);
18291         this.onStartDrag(x, y);
18292         return true;
18293     },
18294
18295     /**
18296      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18297      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18298      */
18299     getProxy : function(){
18300         return this.proxy;  
18301     },
18302
18303     /**
18304      * Hides the drag source's {@link Roo.dd.StatusProxy}
18305      */
18306     hideProxy : function(){
18307         this.proxy.hide();  
18308         this.proxy.reset(true);
18309         this.dragging = false;
18310     },
18311
18312     // private
18313     triggerCacheRefresh : function(){
18314         Roo.dd.DDM.refreshCache(this.groups);
18315     },
18316
18317     // private - override to prevent hiding
18318     b4EndDrag: function(e) {
18319     },
18320
18321     // private - override to prevent moving
18322     endDrag : function(e){
18323         this.onEndDrag(this.dragData, e);
18324     },
18325
18326     // private
18327     onEndDrag : function(data, e){
18328     },
18329     
18330     // private - pin to cursor
18331     autoOffset : function(x, y) {
18332         this.setDelta(-12, -20);
18333     }    
18334 });/*
18335  * Based on:
18336  * Ext JS Library 1.1.1
18337  * Copyright(c) 2006-2007, Ext JS, LLC.
18338  *
18339  * Originally Released Under LGPL - original licence link has changed is not relivant.
18340  *
18341  * Fork - LGPL
18342  * <script type="text/javascript">
18343  */
18344
18345
18346 /**
18347  * @class Roo.dd.DropTarget
18348  * @extends Roo.dd.DDTarget
18349  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18350  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18351  * @constructor
18352  * @param {String/HTMLElement/Element} el The container element
18353  * @param {Object} config
18354  */
18355 Roo.dd.DropTarget = function(el, config){
18356     this.el = Roo.get(el);
18357     
18358     var listeners = false; ;
18359     if (config && config.listeners) {
18360         listeners= config.listeners;
18361         delete config.listeners;
18362     }
18363     Roo.apply(this, config);
18364     
18365     if(this.containerScroll){
18366         Roo.dd.ScrollManager.register(this.el);
18367     }
18368     this.addEvents( {
18369          /**
18370          * @scope Roo.dd.DropTarget
18371          */
18372          
18373          /**
18374          * @event enter
18375          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18376          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18377          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18378          * 
18379          * IMPORTANT : it should set this.overClass and this.dropAllowed
18380          * 
18381          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18382          * @param {Event} e The event
18383          * @param {Object} data An object containing arbitrary data supplied by the drag source
18384          */
18385         "enter" : true,
18386         
18387          /**
18388          * @event over
18389          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18390          * This method will be called on every mouse movement while the drag source is over the drop target.
18391          * This default implementation simply returns the dropAllowed config value.
18392          * 
18393          * IMPORTANT : it should set this.dropAllowed
18394          * 
18395          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18396          * @param {Event} e The event
18397          * @param {Object} data An object containing arbitrary data supplied by the drag source
18398          
18399          */
18400         "over" : true,
18401         /**
18402          * @event out
18403          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18404          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18405          * overClass (if any) from the drop element.
18406          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18407          * @param {Event} e The event
18408          * @param {Object} data An object containing arbitrary data supplied by the drag source
18409          */
18410          "out" : true,
18411          
18412         /**
18413          * @event drop
18414          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18415          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18416          * implementation that does something to process the drop event and returns true so that the drag source's
18417          * repair action does not run.
18418          * 
18419          * IMPORTANT : it should set this.success
18420          * 
18421          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18422          * @param {Event} e The event
18423          * @param {Object} data An object containing arbitrary data supplied by the drag source
18424         */
18425          "drop" : true
18426     });
18427             
18428      
18429     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18430         this.el.dom, 
18431         this.ddGroup || this.group,
18432         {
18433             isTarget: true,
18434             listeners : listeners || {} 
18435            
18436         
18437         }
18438     );
18439
18440 };
18441
18442 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18443     /**
18444      * @cfg {String} overClass
18445      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18446      */
18447      /**
18448      * @cfg {String} ddGroup
18449      * The drag drop group to handle drop events for
18450      */
18451      
18452     /**
18453      * @cfg {String} dropAllowed
18454      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18455      */
18456     dropAllowed : "x-dd-drop-ok",
18457     /**
18458      * @cfg {String} dropNotAllowed
18459      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18460      */
18461     dropNotAllowed : "x-dd-drop-nodrop",
18462     /**
18463      * @cfg {boolean} success
18464      * set this after drop listener.. 
18465      */
18466     success : false,
18467     /**
18468      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18469      * if the drop point is valid for over/enter..
18470      */
18471     valid : false,
18472     // private
18473     isTarget : true,
18474
18475     // private
18476     isNotifyTarget : true,
18477     
18478     /**
18479      * @hide
18480      */
18481     notifyEnter : function(dd, e, data)
18482     {
18483         this.valid = true;
18484         this.fireEvent('enter', dd, e, data);
18485         if(this.overClass){
18486             this.el.addClass(this.overClass);
18487         }
18488         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18489             this.valid ? this.dropAllowed : this.dropNotAllowed
18490         );
18491     },
18492
18493     /**
18494      * @hide
18495      */
18496     notifyOver : function(dd, e, data)
18497     {
18498         this.valid = true;
18499         this.fireEvent('over', dd, e, data);
18500         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18501             this.valid ? this.dropAllowed : this.dropNotAllowed
18502         );
18503     },
18504
18505     /**
18506      * @hide
18507      */
18508     notifyOut : function(dd, e, data)
18509     {
18510         this.fireEvent('out', dd, e, data);
18511         if(this.overClass){
18512             this.el.removeClass(this.overClass);
18513         }
18514     },
18515
18516     /**
18517      * @hide
18518      */
18519     notifyDrop : function(dd, e, data)
18520     {
18521         this.success = false;
18522         this.fireEvent('drop', dd, e, data);
18523         return this.success;
18524     }
18525 });/*
18526  * Based on:
18527  * Ext JS Library 1.1.1
18528  * Copyright(c) 2006-2007, Ext JS, LLC.
18529  *
18530  * Originally Released Under LGPL - original licence link has changed is not relivant.
18531  *
18532  * Fork - LGPL
18533  * <script type="text/javascript">
18534  */
18535
18536
18537 /**
18538  * @class Roo.dd.DragZone
18539  * @extends Roo.dd.DragSource
18540  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18541  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18542  * @constructor
18543  * @param {String/HTMLElement/Element} el The container element
18544  * @param {Object} config
18545  */
18546 Roo.dd.DragZone = function(el, config){
18547     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18548     if(this.containerScroll){
18549         Roo.dd.ScrollManager.register(this.el);
18550     }
18551 };
18552
18553 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18554     /**
18555      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18556      * for auto scrolling during drag operations.
18557      */
18558     /**
18559      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18560      * method after a failed drop (defaults to "c3daf9" - light blue)
18561      */
18562
18563     /**
18564      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18565      * for a valid target to drag based on the mouse down. Override this method
18566      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18567      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18568      * @param {EventObject} e The mouse down event
18569      * @return {Object} The dragData
18570      */
18571     getDragData : function(e){
18572         return Roo.dd.Registry.getHandleFromEvent(e);
18573     },
18574     
18575     /**
18576      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18577      * this.dragData.ddel
18578      * @param {Number} x The x position of the click on the dragged object
18579      * @param {Number} y The y position of the click on the dragged object
18580      * @return {Boolean} true to continue the drag, false to cancel
18581      */
18582     onInitDrag : function(x, y){
18583         this.proxy.update(this.dragData.ddel.cloneNode(true));
18584         this.onStartDrag(x, y);
18585         return true;
18586     },
18587     
18588     /**
18589      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18590      */
18591     afterRepair : function(){
18592         if(Roo.enableFx){
18593             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18594         }
18595         this.dragging = false;
18596     },
18597
18598     /**
18599      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18600      * the XY of this.dragData.ddel
18601      * @param {EventObject} e The mouse up event
18602      * @return {Array} The xy location (e.g. [100, 200])
18603      */
18604     getRepairXY : function(e){
18605         return Roo.Element.fly(this.dragData.ddel).getXY();  
18606     }
18607 });/*
18608  * Based on:
18609  * Ext JS Library 1.1.1
18610  * Copyright(c) 2006-2007, Ext JS, LLC.
18611  *
18612  * Originally Released Under LGPL - original licence link has changed is not relivant.
18613  *
18614  * Fork - LGPL
18615  * <script type="text/javascript">
18616  */
18617 /**
18618  * @class Roo.dd.DropZone
18619  * @extends Roo.dd.DropTarget
18620  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18621  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18622  * @constructor
18623  * @param {String/HTMLElement/Element} el The container element
18624  * @param {Object} config
18625  */
18626 Roo.dd.DropZone = function(el, config){
18627     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18628 };
18629
18630 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18631     /**
18632      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18633      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18634      * provide your own custom lookup.
18635      * @param {Event} e The event
18636      * @return {Object} data The custom data
18637      */
18638     getTargetFromEvent : function(e){
18639         return Roo.dd.Registry.getTargetFromEvent(e);
18640     },
18641
18642     /**
18643      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18644      * that it has registered.  This method has no default implementation and should be overridden to provide
18645      * node-specific processing if necessary.
18646      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18647      * {@link #getTargetFromEvent} for this node)
18648      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18649      * @param {Event} e The event
18650      * @param {Object} data An object containing arbitrary data supplied by the drag source
18651      */
18652     onNodeEnter : function(n, dd, e, data){
18653         
18654     },
18655
18656     /**
18657      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18658      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18659      * overridden to provide the proper feedback.
18660      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18661      * {@link #getTargetFromEvent} for this node)
18662      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18663      * @param {Event} e The event
18664      * @param {Object} data An object containing arbitrary data supplied by the drag source
18665      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18666      * underlying {@link Roo.dd.StatusProxy} can be updated
18667      */
18668     onNodeOver : function(n, dd, e, data){
18669         return this.dropAllowed;
18670     },
18671
18672     /**
18673      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18674      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18675      * node-specific processing if necessary.
18676      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18677      * {@link #getTargetFromEvent} for this node)
18678      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18679      * @param {Event} e The event
18680      * @param {Object} data An object containing arbitrary data supplied by the drag source
18681      */
18682     onNodeOut : function(n, dd, e, data){
18683         
18684     },
18685
18686     /**
18687      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18688      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18689      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18690      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18691      * {@link #getTargetFromEvent} for this node)
18692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18693      * @param {Event} e The event
18694      * @param {Object} data An object containing arbitrary data supplied by the drag source
18695      * @return {Boolean} True if the drop was valid, else false
18696      */
18697     onNodeDrop : function(n, dd, e, data){
18698         return false;
18699     },
18700
18701     /**
18702      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18703      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18704      * it should be overridden to provide the proper feedback if necessary.
18705      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18706      * @param {Event} e The event
18707      * @param {Object} data An object containing arbitrary data supplied by the drag source
18708      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18709      * underlying {@link Roo.dd.StatusProxy} can be updated
18710      */
18711     onContainerOver : function(dd, e, data){
18712         return this.dropNotAllowed;
18713     },
18714
18715     /**
18716      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18717      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18718      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18719      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18720      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18721      * @param {Event} e The event
18722      * @param {Object} data An object containing arbitrary data supplied by the drag source
18723      * @return {Boolean} True if the drop was valid, else false
18724      */
18725     onContainerDrop : function(dd, e, data){
18726         return false;
18727     },
18728
18729     /**
18730      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18731      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18732      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18733      * you should override this method and provide a custom implementation.
18734      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18735      * @param {Event} e The event
18736      * @param {Object} data An object containing arbitrary data supplied by the drag source
18737      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18738      * underlying {@link Roo.dd.StatusProxy} can be updated
18739      */
18740     notifyEnter : function(dd, e, data){
18741         return this.dropNotAllowed;
18742     },
18743
18744     /**
18745      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18746      * This method will be called on every mouse movement while the drag source is over the drop zone.
18747      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18748      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18749      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18750      * registered node, it will call {@link #onContainerOver}.
18751      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18752      * @param {Event} e The event
18753      * @param {Object} data An object containing arbitrary data supplied by the drag source
18754      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18755      * underlying {@link Roo.dd.StatusProxy} can be updated
18756      */
18757     notifyOver : function(dd, e, data){
18758         var n = this.getTargetFromEvent(e);
18759         if(!n){ // not over valid drop target
18760             if(this.lastOverNode){
18761                 this.onNodeOut(this.lastOverNode, dd, e, data);
18762                 this.lastOverNode = null;
18763             }
18764             return this.onContainerOver(dd, e, data);
18765         }
18766         if(this.lastOverNode != n){
18767             if(this.lastOverNode){
18768                 this.onNodeOut(this.lastOverNode, dd, e, data);
18769             }
18770             this.onNodeEnter(n, dd, e, data);
18771             this.lastOverNode = n;
18772         }
18773         return this.onNodeOver(n, dd, e, data);
18774     },
18775
18776     /**
18777      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18778      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18779      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18780      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18781      * @param {Event} e The event
18782      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18783      */
18784     notifyOut : function(dd, e, data){
18785         if(this.lastOverNode){
18786             this.onNodeOut(this.lastOverNode, dd, e, data);
18787             this.lastOverNode = null;
18788         }
18789     },
18790
18791     /**
18792      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18793      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18794      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18795      * otherwise it will call {@link #onContainerDrop}.
18796      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18797      * @param {Event} e The event
18798      * @param {Object} data An object containing arbitrary data supplied by the drag source
18799      * @return {Boolean} True if the drop was valid, else false
18800      */
18801     notifyDrop : function(dd, e, data){
18802         if(this.lastOverNode){
18803             this.onNodeOut(this.lastOverNode, dd, e, data);
18804             this.lastOverNode = null;
18805         }
18806         var n = this.getTargetFromEvent(e);
18807         return n ?
18808             this.onNodeDrop(n, dd, e, data) :
18809             this.onContainerDrop(dd, e, data);
18810     },
18811
18812     // private
18813     triggerCacheRefresh : function(){
18814         Roo.dd.DDM.refreshCache(this.groups);
18815     }  
18816 });/*
18817  * Based on:
18818  * Ext JS Library 1.1.1
18819  * Copyright(c) 2006-2007, Ext JS, LLC.
18820  *
18821  * Originally Released Under LGPL - original licence link has changed is not relivant.
18822  *
18823  * Fork - LGPL
18824  * <script type="text/javascript">
18825  */
18826
18827
18828 /**
18829  * @class Roo.data.SortTypes
18830  * @singleton
18831  * Defines the default sorting (casting?) comparison functions used when sorting data.
18832  */
18833 Roo.data.SortTypes = {
18834     /**
18835      * Default sort that does nothing
18836      * @param {Mixed} s The value being converted
18837      * @return {Mixed} The comparison value
18838      */
18839     none : function(s){
18840         return s;
18841     },
18842     
18843     /**
18844      * The regular expression used to strip tags
18845      * @type {RegExp}
18846      * @property
18847      */
18848     stripTagsRE : /<\/?[^>]+>/gi,
18849     
18850     /**
18851      * Strips all HTML tags to sort on text only
18852      * @param {Mixed} s The value being converted
18853      * @return {String} The comparison value
18854      */
18855     asText : function(s){
18856         return String(s).replace(this.stripTagsRE, "");
18857     },
18858     
18859     /**
18860      * Strips all HTML tags to sort on text only - Case insensitive
18861      * @param {Mixed} s The value being converted
18862      * @return {String} The comparison value
18863      */
18864     asUCText : function(s){
18865         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18866     },
18867     
18868     /**
18869      * Case insensitive string
18870      * @param {Mixed} s The value being converted
18871      * @return {String} The comparison value
18872      */
18873     asUCString : function(s) {
18874         return String(s).toUpperCase();
18875     },
18876     
18877     /**
18878      * Date sorting
18879      * @param {Mixed} s The value being converted
18880      * @return {Number} The comparison value
18881      */
18882     asDate : function(s) {
18883         if(!s){
18884             return 0;
18885         }
18886         if(s instanceof Date){
18887             return s.getTime();
18888         }
18889         return Date.parse(String(s));
18890     },
18891     
18892     /**
18893      * Float sorting
18894      * @param {Mixed} s The value being converted
18895      * @return {Float} The comparison value
18896      */
18897     asFloat : function(s) {
18898         var val = parseFloat(String(s).replace(/,/g, ""));
18899         if(isNaN(val)) val = 0;
18900         return val;
18901     },
18902     
18903     /**
18904      * Integer sorting
18905      * @param {Mixed} s The value being converted
18906      * @return {Number} The comparison value
18907      */
18908     asInt : function(s) {
18909         var val = parseInt(String(s).replace(/,/g, ""));
18910         if(isNaN(val)) val = 0;
18911         return val;
18912     }
18913 };/*
18914  * Based on:
18915  * Ext JS Library 1.1.1
18916  * Copyright(c) 2006-2007, Ext JS, LLC.
18917  *
18918  * Originally Released Under LGPL - original licence link has changed is not relivant.
18919  *
18920  * Fork - LGPL
18921  * <script type="text/javascript">
18922  */
18923
18924 /**
18925 * @class Roo.data.Record
18926  * Instances of this class encapsulate both record <em>definition</em> information, and record
18927  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18928  * to access Records cached in an {@link Roo.data.Store} object.<br>
18929  * <p>
18930  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18931  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18932  * objects.<br>
18933  * <p>
18934  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18935  * @constructor
18936  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18937  * {@link #create}. The parameters are the same.
18938  * @param {Array} data An associative Array of data values keyed by the field name.
18939  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18940  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18941  * not specified an integer id is generated.
18942  */
18943 Roo.data.Record = function(data, id){
18944     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18945     this.data = data;
18946 };
18947
18948 /**
18949  * Generate a constructor for a specific record layout.
18950  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18951  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18952  * Each field definition object may contain the following properties: <ul>
18953  * <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,
18954  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18955  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18956  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18957  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18958  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18959  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18960  * this may be omitted.</p></li>
18961  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18962  * <ul><li>auto (Default, implies no conversion)</li>
18963  * <li>string</li>
18964  * <li>int</li>
18965  * <li>float</li>
18966  * <li>boolean</li>
18967  * <li>date</li></ul></p></li>
18968  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18969  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18970  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18971  * by the Reader into an object that will be stored in the Record. It is passed the
18972  * following parameters:<ul>
18973  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18974  * </ul></p></li>
18975  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18976  * </ul>
18977  * <br>usage:<br><pre><code>
18978 var TopicRecord = Roo.data.Record.create(
18979     {name: 'title', mapping: 'topic_title'},
18980     {name: 'author', mapping: 'username'},
18981     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18982     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18983     {name: 'lastPoster', mapping: 'user2'},
18984     {name: 'excerpt', mapping: 'post_text'}
18985 );
18986
18987 var myNewRecord = new TopicRecord({
18988     title: 'Do my job please',
18989     author: 'noobie',
18990     totalPosts: 1,
18991     lastPost: new Date(),
18992     lastPoster: 'Animal',
18993     excerpt: 'No way dude!'
18994 });
18995 myStore.add(myNewRecord);
18996 </code></pre>
18997  * @method create
18998  * @static
18999  */
19000 Roo.data.Record.create = function(o){
19001     var f = function(){
19002         f.superclass.constructor.apply(this, arguments);
19003     };
19004     Roo.extend(f, Roo.data.Record);
19005     var p = f.prototype;
19006     p.fields = new Roo.util.MixedCollection(false, function(field){
19007         return field.name;
19008     });
19009     for(var i = 0, len = o.length; i < len; i++){
19010         p.fields.add(new Roo.data.Field(o[i]));
19011     }
19012     f.getField = function(name){
19013         return p.fields.get(name);  
19014     };
19015     return f;
19016 };
19017
19018 Roo.data.Record.AUTO_ID = 1000;
19019 Roo.data.Record.EDIT = 'edit';
19020 Roo.data.Record.REJECT = 'reject';
19021 Roo.data.Record.COMMIT = 'commit';
19022
19023 Roo.data.Record.prototype = {
19024     /**
19025      * Readonly flag - true if this record has been modified.
19026      * @type Boolean
19027      */
19028     dirty : false,
19029     editing : false,
19030     error: null,
19031     modified: null,
19032
19033     // private
19034     join : function(store){
19035         this.store = store;
19036     },
19037
19038     /**
19039      * Set the named field to the specified value.
19040      * @param {String} name The name of the field to set.
19041      * @param {Object} value The value to set the field to.
19042      */
19043     set : function(name, value){
19044         if(this.data[name] == value){
19045             return;
19046         }
19047         this.dirty = true;
19048         if(!this.modified){
19049             this.modified = {};
19050         }
19051         if(typeof this.modified[name] == 'undefined'){
19052             this.modified[name] = this.data[name];
19053         }
19054         this.data[name] = value;
19055         if(!this.editing && this.store){
19056             this.store.afterEdit(this);
19057         }       
19058     },
19059
19060     /**
19061      * Get the value of the named field.
19062      * @param {String} name The name of the field to get the value of.
19063      * @return {Object} The value of the field.
19064      */
19065     get : function(name){
19066         return this.data[name]; 
19067     },
19068
19069     // private
19070     beginEdit : function(){
19071         this.editing = true;
19072         this.modified = {}; 
19073     },
19074
19075     // private
19076     cancelEdit : function(){
19077         this.editing = false;
19078         delete this.modified;
19079     },
19080
19081     // private
19082     endEdit : function(){
19083         this.editing = false;
19084         if(this.dirty && this.store){
19085             this.store.afterEdit(this);
19086         }
19087     },
19088
19089     /**
19090      * Usually called by the {@link Roo.data.Store} which owns the Record.
19091      * Rejects all changes made to the Record since either creation, or the last commit operation.
19092      * Modified fields are reverted to their original values.
19093      * <p>
19094      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19095      * of reject operations.
19096      */
19097     reject : function(){
19098         var m = this.modified;
19099         for(var n in m){
19100             if(typeof m[n] != "function"){
19101                 this.data[n] = m[n];
19102             }
19103         }
19104         this.dirty = false;
19105         delete this.modified;
19106         this.editing = false;
19107         if(this.store){
19108             this.store.afterReject(this);
19109         }
19110     },
19111
19112     /**
19113      * Usually called by the {@link Roo.data.Store} which owns the Record.
19114      * Commits all changes made to the Record since either creation, or the last commit operation.
19115      * <p>
19116      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19117      * of commit operations.
19118      */
19119     commit : function(){
19120         this.dirty = false;
19121         delete this.modified;
19122         this.editing = false;
19123         if(this.store){
19124             this.store.afterCommit(this);
19125         }
19126     },
19127
19128     // private
19129     hasError : function(){
19130         return this.error != null;
19131     },
19132
19133     // private
19134     clearError : function(){
19135         this.error = null;
19136     },
19137
19138     /**
19139      * Creates a copy of this record.
19140      * @param {String} id (optional) A new record id if you don't want to use this record's id
19141      * @return {Record}
19142      */
19143     copy : function(newId) {
19144         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19145     }
19146 };/*
19147  * Based on:
19148  * Ext JS Library 1.1.1
19149  * Copyright(c) 2006-2007, Ext JS, LLC.
19150  *
19151  * Originally Released Under LGPL - original licence link has changed is not relivant.
19152  *
19153  * Fork - LGPL
19154  * <script type="text/javascript">
19155  */
19156
19157
19158
19159 /**
19160  * @class Roo.data.Store
19161  * @extends Roo.util.Observable
19162  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19163  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19164  * <p>
19165  * 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
19166  * has no knowledge of the format of the data returned by the Proxy.<br>
19167  * <p>
19168  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19169  * instances from the data object. These records are cached and made available through accessor functions.
19170  * @constructor
19171  * Creates a new Store.
19172  * @param {Object} config A config object containing the objects needed for the Store to access data,
19173  * and read the data into Records.
19174  */
19175 Roo.data.Store = function(config){
19176     this.data = new Roo.util.MixedCollection(false);
19177     this.data.getKey = function(o){
19178         return o.id;
19179     };
19180     this.baseParams = {};
19181     // private
19182     this.paramNames = {
19183         "start" : "start",
19184         "limit" : "limit",
19185         "sort" : "sort",
19186         "dir" : "dir",
19187         "multisort" : "_multisort"
19188     };
19189
19190     if(config && config.data){
19191         this.inlineData = config.data;
19192         delete config.data;
19193     }
19194
19195     Roo.apply(this, config);
19196     
19197     if(this.reader){ // reader passed
19198         this.reader = Roo.factory(this.reader, Roo.data);
19199         this.reader.xmodule = this.xmodule || false;
19200         if(!this.recordType){
19201             this.recordType = this.reader.recordType;
19202         }
19203         if(this.reader.onMetaChange){
19204             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19205         }
19206     }
19207
19208     if(this.recordType){
19209         this.fields = this.recordType.prototype.fields;
19210     }
19211     this.modified = [];
19212
19213     this.addEvents({
19214         /**
19215          * @event datachanged
19216          * Fires when the data cache has changed, and a widget which is using this Store
19217          * as a Record cache should refresh its view.
19218          * @param {Store} this
19219          */
19220         datachanged : true,
19221         /**
19222          * @event metachange
19223          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19224          * @param {Store} this
19225          * @param {Object} meta The JSON metadata
19226          */
19227         metachange : true,
19228         /**
19229          * @event add
19230          * Fires when Records have been added to the Store
19231          * @param {Store} this
19232          * @param {Roo.data.Record[]} records The array of Records added
19233          * @param {Number} index The index at which the record(s) were added
19234          */
19235         add : true,
19236         /**
19237          * @event remove
19238          * Fires when a Record has been removed from the Store
19239          * @param {Store} this
19240          * @param {Roo.data.Record} record The Record that was removed
19241          * @param {Number} index The index at which the record was removed
19242          */
19243         remove : true,
19244         /**
19245          * @event update
19246          * Fires when a Record has been updated
19247          * @param {Store} this
19248          * @param {Roo.data.Record} record The Record that was updated
19249          * @param {String} operation The update operation being performed.  Value may be one of:
19250          * <pre><code>
19251  Roo.data.Record.EDIT
19252  Roo.data.Record.REJECT
19253  Roo.data.Record.COMMIT
19254          * </code></pre>
19255          */
19256         update : true,
19257         /**
19258          * @event clear
19259          * Fires when the data cache has been cleared.
19260          * @param {Store} this
19261          */
19262         clear : true,
19263         /**
19264          * @event beforeload
19265          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19266          * the load action will be canceled.
19267          * @param {Store} this
19268          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19269          */
19270         beforeload : true,
19271         /**
19272          * @event load
19273          * Fires after a new set of Records has been loaded.
19274          * @param {Store} this
19275          * @param {Roo.data.Record[]} records The Records that were loaded
19276          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19277          */
19278         load : true,
19279         /**
19280          * @event loadexception
19281          * Fires if an exception occurs in the Proxy during loading.
19282          * Called with the signature of the Proxy's "loadexception" event.
19283          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19284          * 
19285          * @param {Proxy} 
19286          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19287          * @param {Object} load options 
19288          * @param {Object} jsonData from your request (normally this contains the Exception)
19289          */
19290         loadexception : true
19291     });
19292     
19293     if(this.proxy){
19294         this.proxy = Roo.factory(this.proxy, Roo.data);
19295         this.proxy.xmodule = this.xmodule || false;
19296         this.relayEvents(this.proxy,  ["loadexception"]);
19297     }
19298     this.sortToggle = {};
19299     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19300
19301     Roo.data.Store.superclass.constructor.call(this);
19302
19303     if(this.inlineData){
19304         this.loadData(this.inlineData);
19305         delete this.inlineData;
19306     }
19307 };
19308 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19309      /**
19310     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19311     * without a remote query - used by combo/forms at present.
19312     */
19313     
19314     /**
19315     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19316     */
19317     /**
19318     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19319     */
19320     /**
19321     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19322     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19323     */
19324     /**
19325     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19326     * on any HTTP request
19327     */
19328     /**
19329     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19330     */
19331     /**
19332     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19333     */
19334     multiSort: false,
19335     /**
19336     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19337     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19338     */
19339     remoteSort : false,
19340
19341     /**
19342     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19343      * loaded or when a record is removed. (defaults to false).
19344     */
19345     pruneModifiedRecords : false,
19346
19347     // private
19348     lastOptions : null,
19349
19350     /**
19351      * Add Records to the Store and fires the add event.
19352      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19353      */
19354     add : function(records){
19355         records = [].concat(records);
19356         for(var i = 0, len = records.length; i < len; i++){
19357             records[i].join(this);
19358         }
19359         var index = this.data.length;
19360         this.data.addAll(records);
19361         this.fireEvent("add", this, records, index);
19362     },
19363
19364     /**
19365      * Remove a Record from the Store and fires the remove event.
19366      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19367      */
19368     remove : function(record){
19369         var index = this.data.indexOf(record);
19370         this.data.removeAt(index);
19371         if(this.pruneModifiedRecords){
19372             this.modified.remove(record);
19373         }
19374         this.fireEvent("remove", this, record, index);
19375     },
19376
19377     /**
19378      * Remove all Records from the Store and fires the clear event.
19379      */
19380     removeAll : function(){
19381         this.data.clear();
19382         if(this.pruneModifiedRecords){
19383             this.modified = [];
19384         }
19385         this.fireEvent("clear", this);
19386     },
19387
19388     /**
19389      * Inserts Records to the Store at the given index and fires the add event.
19390      * @param {Number} index The start index at which to insert the passed Records.
19391      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19392      */
19393     insert : function(index, records){
19394         records = [].concat(records);
19395         for(var i = 0, len = records.length; i < len; i++){
19396             this.data.insert(index, records[i]);
19397             records[i].join(this);
19398         }
19399         this.fireEvent("add", this, records, index);
19400     },
19401
19402     /**
19403      * Get the index within the cache of the passed Record.
19404      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19405      * @return {Number} The index of the passed Record. Returns -1 if not found.
19406      */
19407     indexOf : function(record){
19408         return this.data.indexOf(record);
19409     },
19410
19411     /**
19412      * Get the index within the cache of the Record with the passed id.
19413      * @param {String} id The id of the Record to find.
19414      * @return {Number} The index of the Record. Returns -1 if not found.
19415      */
19416     indexOfId : function(id){
19417         return this.data.indexOfKey(id);
19418     },
19419
19420     /**
19421      * Get the Record with the specified id.
19422      * @param {String} id The id of the Record to find.
19423      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19424      */
19425     getById : function(id){
19426         return this.data.key(id);
19427     },
19428
19429     /**
19430      * Get the Record at the specified index.
19431      * @param {Number} index The index of the Record to find.
19432      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19433      */
19434     getAt : function(index){
19435         return this.data.itemAt(index);
19436     },
19437
19438     /**
19439      * Returns a range of Records between specified indices.
19440      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19441      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19442      * @return {Roo.data.Record[]} An array of Records
19443      */
19444     getRange : function(start, end){
19445         return this.data.getRange(start, end);
19446     },
19447
19448     // private
19449     storeOptions : function(o){
19450         o = Roo.apply({}, o);
19451         delete o.callback;
19452         delete o.scope;
19453         this.lastOptions = o;
19454     },
19455
19456     /**
19457      * Loads the Record cache from the configured Proxy using the configured Reader.
19458      * <p>
19459      * If using remote paging, then the first load call must specify the <em>start</em>
19460      * and <em>limit</em> properties in the options.params property to establish the initial
19461      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19462      * <p>
19463      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19464      * and this call will return before the new data has been loaded. Perform any post-processing
19465      * in a callback function, or in a "load" event handler.</strong>
19466      * <p>
19467      * @param {Object} options An object containing properties which control loading options:<ul>
19468      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19469      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19470      * passed the following arguments:<ul>
19471      * <li>r : Roo.data.Record[]</li>
19472      * <li>options: Options object from the load call</li>
19473      * <li>success: Boolean success indicator</li></ul></li>
19474      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19475      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19476      * </ul>
19477      */
19478     load : function(options){
19479         options = options || {};
19480         if(this.fireEvent("beforeload", this, options) !== false){
19481             this.storeOptions(options);
19482             var p = Roo.apply(options.params || {}, this.baseParams);
19483             // if meta was not loaded from remote source.. try requesting it.
19484             if (!this.reader.metaFromRemote) {
19485                 p._requestMeta = 1;
19486             }
19487             if(this.sortInfo && this.remoteSort){
19488                 var pn = this.paramNames;
19489                 p[pn["sort"]] = this.sortInfo.field;
19490                 p[pn["dir"]] = this.sortInfo.direction;
19491             }
19492             if (this.multiSort) {
19493                 var pn = this.paramNames;
19494                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19495             }
19496             
19497             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19498         }
19499     },
19500
19501     /**
19502      * Reloads the Record cache from the configured Proxy using the configured Reader and
19503      * the options from the last load operation performed.
19504      * @param {Object} options (optional) An object containing properties which may override the options
19505      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19506      * the most recently used options are reused).
19507      */
19508     reload : function(options){
19509         this.load(Roo.applyIf(options||{}, this.lastOptions));
19510     },
19511
19512     // private
19513     // Called as a callback by the Reader during a load operation.
19514     loadRecords : function(o, options, success){
19515         if(!o || success === false){
19516             if(success !== false){
19517                 this.fireEvent("load", this, [], options);
19518             }
19519             if(options.callback){
19520                 options.callback.call(options.scope || this, [], options, false);
19521             }
19522             return;
19523         }
19524         // if data returned failure - throw an exception.
19525         if (o.success === false) {
19526             // show a message if no listener is registered.
19527             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19528                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19529             }
19530             // loadmask wil be hooked into this..
19531             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19532             return;
19533         }
19534         var r = o.records, t = o.totalRecords || r.length;
19535         if(!options || options.add !== true){
19536             if(this.pruneModifiedRecords){
19537                 this.modified = [];
19538             }
19539             for(var i = 0, len = r.length; i < len; i++){
19540                 r[i].join(this);
19541             }
19542             if(this.snapshot){
19543                 this.data = this.snapshot;
19544                 delete this.snapshot;
19545             }
19546             this.data.clear();
19547             this.data.addAll(r);
19548             this.totalLength = t;
19549             this.applySort();
19550             this.fireEvent("datachanged", this);
19551         }else{
19552             this.totalLength = Math.max(t, this.data.length+r.length);
19553             this.add(r);
19554         }
19555         this.fireEvent("load", this, r, options);
19556         if(options.callback){
19557             options.callback.call(options.scope || this, r, options, true);
19558         }
19559     },
19560
19561
19562     /**
19563      * Loads data from a passed data block. A Reader which understands the format of the data
19564      * must have been configured in the constructor.
19565      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19566      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19567      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19568      */
19569     loadData : function(o, append){
19570         var r = this.reader.readRecords(o);
19571         this.loadRecords(r, {add: append}, true);
19572     },
19573
19574     /**
19575      * Gets the number of cached records.
19576      * <p>
19577      * <em>If using paging, this may not be the total size of the dataset. If the data object
19578      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19579      * the data set size</em>
19580      */
19581     getCount : function(){
19582         return this.data.length || 0;
19583     },
19584
19585     /**
19586      * Gets the total number of records in the dataset as returned by the server.
19587      * <p>
19588      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19589      * the dataset size</em>
19590      */
19591     getTotalCount : function(){
19592         return this.totalLength || 0;
19593     },
19594
19595     /**
19596      * Returns the sort state of the Store as an object with two properties:
19597      * <pre><code>
19598  field {String} The name of the field by which the Records are sorted
19599  direction {String} The sort order, "ASC" or "DESC"
19600      * </code></pre>
19601      */
19602     getSortState : function(){
19603         return this.sortInfo;
19604     },
19605
19606     // private
19607     applySort : function(){
19608         if(this.sortInfo && !this.remoteSort){
19609             var s = this.sortInfo, f = s.field;
19610             var st = this.fields.get(f).sortType;
19611             var fn = function(r1, r2){
19612                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19613                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19614             };
19615             this.data.sort(s.direction, fn);
19616             if(this.snapshot && this.snapshot != this.data){
19617                 this.snapshot.sort(s.direction, fn);
19618             }
19619         }
19620     },
19621
19622     /**
19623      * Sets the default sort column and order to be used by the next load operation.
19624      * @param {String} fieldName The name of the field to sort by.
19625      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19626      */
19627     setDefaultSort : function(field, dir){
19628         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19629     },
19630
19631     /**
19632      * Sort the Records.
19633      * If remote sorting is used, the sort is performed on the server, and the cache is
19634      * reloaded. If local sorting is used, the cache is sorted internally.
19635      * @param {String} fieldName The name of the field to sort by.
19636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19637      */
19638     sort : function(fieldName, dir){
19639         var f = this.fields.get(fieldName);
19640         if(!dir){
19641             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19642             
19643             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19644                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19645             }else{
19646                 dir = f.sortDir;
19647             }
19648         }
19649         this.sortToggle[f.name] = dir;
19650         this.sortInfo = {field: f.name, direction: dir};
19651         if(!this.remoteSort){
19652             this.applySort();
19653             this.fireEvent("datachanged", this);
19654         }else{
19655             this.load(this.lastOptions);
19656         }
19657     },
19658
19659     /**
19660      * Calls the specified function for each of the Records in the cache.
19661      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19662      * Returning <em>false</em> aborts and exits the iteration.
19663      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19664      */
19665     each : function(fn, scope){
19666         this.data.each(fn, scope);
19667     },
19668
19669     /**
19670      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19671      * (e.g., during paging).
19672      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19673      */
19674     getModifiedRecords : function(){
19675         return this.modified;
19676     },
19677
19678     // private
19679     createFilterFn : function(property, value, anyMatch){
19680         if(!value.exec){ // not a regex
19681             value = String(value);
19682             if(value.length == 0){
19683                 return false;
19684             }
19685             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19686         }
19687         return function(r){
19688             return value.test(r.data[property]);
19689         };
19690     },
19691
19692     /**
19693      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19694      * @param {String} property A field on your records
19695      * @param {Number} start The record index to start at (defaults to 0)
19696      * @param {Number} end The last record index to include (defaults to length - 1)
19697      * @return {Number} The sum
19698      */
19699     sum : function(property, start, end){
19700         var rs = this.data.items, v = 0;
19701         start = start || 0;
19702         end = (end || end === 0) ? end : rs.length-1;
19703
19704         for(var i = start; i <= end; i++){
19705             v += (rs[i].data[property] || 0);
19706         }
19707         return v;
19708     },
19709
19710     /**
19711      * Filter the records by a specified property.
19712      * @param {String} field A field on your records
19713      * @param {String/RegExp} value Either a string that the field
19714      * should start with or a RegExp to test against the field
19715      * @param {Boolean} anyMatch True to match any part not just the beginning
19716      */
19717     filter : function(property, value, anyMatch){
19718         var fn = this.createFilterFn(property, value, anyMatch);
19719         return fn ? this.filterBy(fn) : this.clearFilter();
19720     },
19721
19722     /**
19723      * Filter by a function. The specified function will be called with each
19724      * record in this data source. If the function returns true the record is included,
19725      * otherwise it is filtered.
19726      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19727      * @param {Object} scope (optional) The scope of the function (defaults to this)
19728      */
19729     filterBy : function(fn, scope){
19730         this.snapshot = this.snapshot || this.data;
19731         this.data = this.queryBy(fn, scope||this);
19732         this.fireEvent("datachanged", this);
19733     },
19734
19735     /**
19736      * Query the records by a specified property.
19737      * @param {String} field A field on your records
19738      * @param {String/RegExp} value Either a string that the field
19739      * should start with or a RegExp to test against the field
19740      * @param {Boolean} anyMatch True to match any part not just the beginning
19741      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19742      */
19743     query : function(property, value, anyMatch){
19744         var fn = this.createFilterFn(property, value, anyMatch);
19745         return fn ? this.queryBy(fn) : this.data.clone();
19746     },
19747
19748     /**
19749      * Query by a function. The specified function will be called with each
19750      * record in this data source. If the function returns true the record is included
19751      * in the results.
19752      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19753      * @param {Object} scope (optional) The scope of the function (defaults to this)
19754       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19755      **/
19756     queryBy : function(fn, scope){
19757         var data = this.snapshot || this.data;
19758         return data.filterBy(fn, scope||this);
19759     },
19760
19761     /**
19762      * Collects unique values for a particular dataIndex from this store.
19763      * @param {String} dataIndex The property to collect
19764      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19765      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19766      * @return {Array} An array of the unique values
19767      **/
19768     collect : function(dataIndex, allowNull, bypassFilter){
19769         var d = (bypassFilter === true && this.snapshot) ?
19770                 this.snapshot.items : this.data.items;
19771         var v, sv, r = [], l = {};
19772         for(var i = 0, len = d.length; i < len; i++){
19773             v = d[i].data[dataIndex];
19774             sv = String(v);
19775             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19776                 l[sv] = true;
19777                 r[r.length] = v;
19778             }
19779         }
19780         return r;
19781     },
19782
19783     /**
19784      * Revert to a view of the Record cache with no filtering applied.
19785      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19786      */
19787     clearFilter : function(suppressEvent){
19788         if(this.snapshot && this.snapshot != this.data){
19789             this.data = this.snapshot;
19790             delete this.snapshot;
19791             if(suppressEvent !== true){
19792                 this.fireEvent("datachanged", this);
19793             }
19794         }
19795     },
19796
19797     // private
19798     afterEdit : function(record){
19799         if(this.modified.indexOf(record) == -1){
19800             this.modified.push(record);
19801         }
19802         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19803     },
19804     
19805     // private
19806     afterReject : function(record){
19807         this.modified.remove(record);
19808         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19809     },
19810
19811     // private
19812     afterCommit : function(record){
19813         this.modified.remove(record);
19814         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19815     },
19816
19817     /**
19818      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19819      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19820      */
19821     commitChanges : function(){
19822         var m = this.modified.slice(0);
19823         this.modified = [];
19824         for(var i = 0, len = m.length; i < len; i++){
19825             m[i].commit();
19826         }
19827     },
19828
19829     /**
19830      * Cancel outstanding changes on all changed records.
19831      */
19832     rejectChanges : function(){
19833         var m = this.modified.slice(0);
19834         this.modified = [];
19835         for(var i = 0, len = m.length; i < len; i++){
19836             m[i].reject();
19837         }
19838     },
19839
19840     onMetaChange : function(meta, rtype, o){
19841         this.recordType = rtype;
19842         this.fields = rtype.prototype.fields;
19843         delete this.snapshot;
19844         this.sortInfo = meta.sortInfo || this.sortInfo;
19845         this.modified = [];
19846         this.fireEvent('metachange', this, this.reader.meta);
19847     }
19848 });/*
19849  * Based on:
19850  * Ext JS Library 1.1.1
19851  * Copyright(c) 2006-2007, Ext JS, LLC.
19852  *
19853  * Originally Released Under LGPL - original licence link has changed is not relivant.
19854  *
19855  * Fork - LGPL
19856  * <script type="text/javascript">
19857  */
19858
19859 /**
19860  * @class Roo.data.SimpleStore
19861  * @extends Roo.data.Store
19862  * Small helper class to make creating Stores from Array data easier.
19863  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19864  * @cfg {Array} fields An array of field definition objects, or field name strings.
19865  * @cfg {Array} data The multi-dimensional array of data
19866  * @constructor
19867  * @param {Object} config
19868  */
19869 Roo.data.SimpleStore = function(config){
19870     Roo.data.SimpleStore.superclass.constructor.call(this, {
19871         isLocal : true,
19872         reader: new Roo.data.ArrayReader({
19873                 id: config.id
19874             },
19875             Roo.data.Record.create(config.fields)
19876         ),
19877         proxy : new Roo.data.MemoryProxy(config.data)
19878     });
19879     this.load();
19880 };
19881 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19882  * Based on:
19883  * Ext JS Library 1.1.1
19884  * Copyright(c) 2006-2007, Ext JS, LLC.
19885  *
19886  * Originally Released Under LGPL - original licence link has changed is not relivant.
19887  *
19888  * Fork - LGPL
19889  * <script type="text/javascript">
19890  */
19891
19892 /**
19893 /**
19894  * @extends Roo.data.Store
19895  * @class Roo.data.JsonStore
19896  * Small helper class to make creating Stores for JSON data easier. <br/>
19897 <pre><code>
19898 var store = new Roo.data.JsonStore({
19899     url: 'get-images.php',
19900     root: 'images',
19901     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19902 });
19903 </code></pre>
19904  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19905  * JsonReader and HttpProxy (unless inline data is provided).</b>
19906  * @cfg {Array} fields An array of field definition objects, or field name strings.
19907  * @constructor
19908  * @param {Object} config
19909  */
19910 Roo.data.JsonStore = function(c){
19911     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19912         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19913         reader: new Roo.data.JsonReader(c, c.fields)
19914     }));
19915 };
19916 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19917  * Based on:
19918  * Ext JS Library 1.1.1
19919  * Copyright(c) 2006-2007, Ext JS, LLC.
19920  *
19921  * Originally Released Under LGPL - original licence link has changed is not relivant.
19922  *
19923  * Fork - LGPL
19924  * <script type="text/javascript">
19925  */
19926
19927  
19928 Roo.data.Field = function(config){
19929     if(typeof config == "string"){
19930         config = {name: config};
19931     }
19932     Roo.apply(this, config);
19933     
19934     if(!this.type){
19935         this.type = "auto";
19936     }
19937     
19938     var st = Roo.data.SortTypes;
19939     // named sortTypes are supported, here we look them up
19940     if(typeof this.sortType == "string"){
19941         this.sortType = st[this.sortType];
19942     }
19943     
19944     // set default sortType for strings and dates
19945     if(!this.sortType){
19946         switch(this.type){
19947             case "string":
19948                 this.sortType = st.asUCString;
19949                 break;
19950             case "date":
19951                 this.sortType = st.asDate;
19952                 break;
19953             default:
19954                 this.sortType = st.none;
19955         }
19956     }
19957
19958     // define once
19959     var stripRe = /[\$,%]/g;
19960
19961     // prebuilt conversion function for this field, instead of
19962     // switching every time we're reading a value
19963     if(!this.convert){
19964         var cv, dateFormat = this.dateFormat;
19965         switch(this.type){
19966             case "":
19967             case "auto":
19968             case undefined:
19969                 cv = function(v){ return v; };
19970                 break;
19971             case "string":
19972                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19973                 break;
19974             case "int":
19975                 cv = function(v){
19976                     return v !== undefined && v !== null && v !== '' ?
19977                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19978                     };
19979                 break;
19980             case "float":
19981                 cv = function(v){
19982                     return v !== undefined && v !== null && v !== '' ?
19983                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19984                     };
19985                 break;
19986             case "bool":
19987             case "boolean":
19988                 cv = function(v){ return v === true || v === "true" || v == 1; };
19989                 break;
19990             case "date":
19991                 cv = function(v){
19992                     if(!v){
19993                         return '';
19994                     }
19995                     if(v instanceof Date){
19996                         return v;
19997                     }
19998                     if(dateFormat){
19999                         if(dateFormat == "timestamp"){
20000                             return new Date(v*1000);
20001                         }
20002                         return Date.parseDate(v, dateFormat);
20003                     }
20004                     var parsed = Date.parse(v);
20005                     return parsed ? new Date(parsed) : null;
20006                 };
20007              break;
20008             
20009         }
20010         this.convert = cv;
20011     }
20012 };
20013
20014 Roo.data.Field.prototype = {
20015     dateFormat: null,
20016     defaultValue: "",
20017     mapping: null,
20018     sortType : null,
20019     sortDir : "ASC"
20020 };/*
20021  * Based on:
20022  * Ext JS Library 1.1.1
20023  * Copyright(c) 2006-2007, Ext JS, LLC.
20024  *
20025  * Originally Released Under LGPL - original licence link has changed is not relivant.
20026  *
20027  * Fork - LGPL
20028  * <script type="text/javascript">
20029  */
20030  
20031 // Base class for reading structured data from a data source.  This class is intended to be
20032 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20033
20034 /**
20035  * @class Roo.data.DataReader
20036  * Base class for reading structured data from a data source.  This class is intended to be
20037  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20038  */
20039
20040 Roo.data.DataReader = function(meta, recordType){
20041     
20042     this.meta = meta;
20043     
20044     this.recordType = recordType instanceof Array ? 
20045         Roo.data.Record.create(recordType) : recordType;
20046 };
20047
20048 Roo.data.DataReader.prototype = {
20049      /**
20050      * Create an empty record
20051      * @param {Object} data (optional) - overlay some values
20052      * @return {Roo.data.Record} record created.
20053      */
20054     newRow :  function(d) {
20055         var da =  {};
20056         this.recordType.prototype.fields.each(function(c) {
20057             switch( c.type) {
20058                 case 'int' : da[c.name] = 0; break;
20059                 case 'date' : da[c.name] = new Date(); break;
20060                 case 'float' : da[c.name] = 0.0; break;
20061                 case 'boolean' : da[c.name] = false; break;
20062                 default : da[c.name] = ""; break;
20063             }
20064             
20065         });
20066         return new this.recordType(Roo.apply(da, d));
20067     }
20068     
20069 };/*
20070  * Based on:
20071  * Ext JS Library 1.1.1
20072  * Copyright(c) 2006-2007, Ext JS, LLC.
20073  *
20074  * Originally Released Under LGPL - original licence link has changed is not relivant.
20075  *
20076  * Fork - LGPL
20077  * <script type="text/javascript">
20078  */
20079
20080 /**
20081  * @class Roo.data.DataProxy
20082  * @extends Roo.data.Observable
20083  * This class is an abstract base class for implementations which provide retrieval of
20084  * unformatted data objects.<br>
20085  * <p>
20086  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20087  * (of the appropriate type which knows how to parse the data object) to provide a block of
20088  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20089  * <p>
20090  * Custom implementations must implement the load method as described in
20091  * {@link Roo.data.HttpProxy#load}.
20092  */
20093 Roo.data.DataProxy = function(){
20094     this.addEvents({
20095         /**
20096          * @event beforeload
20097          * Fires before a network request is made to retrieve a data object.
20098          * @param {Object} This DataProxy object.
20099          * @param {Object} params The params parameter to the load function.
20100          */
20101         beforeload : true,
20102         /**
20103          * @event load
20104          * Fires before the load method's callback is called.
20105          * @param {Object} This DataProxy object.
20106          * @param {Object} o The data object.
20107          * @param {Object} arg The callback argument object passed to the load function.
20108          */
20109         load : true,
20110         /**
20111          * @event loadexception
20112          * Fires if an Exception occurs during data retrieval.
20113          * @param {Object} This DataProxy object.
20114          * @param {Object} o The data object.
20115          * @param {Object} arg The callback argument object passed to the load function.
20116          * @param {Object} e The Exception.
20117          */
20118         loadexception : true
20119     });
20120     Roo.data.DataProxy.superclass.constructor.call(this);
20121 };
20122
20123 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20124
20125     /**
20126      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20127      */
20128 /*
20129  * Based on:
20130  * Ext JS Library 1.1.1
20131  * Copyright(c) 2006-2007, Ext JS, LLC.
20132  *
20133  * Originally Released Under LGPL - original licence link has changed is not relivant.
20134  *
20135  * Fork - LGPL
20136  * <script type="text/javascript">
20137  */
20138 /**
20139  * @class Roo.data.MemoryProxy
20140  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20141  * to the Reader when its load method is called.
20142  * @constructor
20143  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20144  */
20145 Roo.data.MemoryProxy = function(data){
20146     if (data.data) {
20147         data = data.data;
20148     }
20149     Roo.data.MemoryProxy.superclass.constructor.call(this);
20150     this.data = data;
20151 };
20152
20153 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20154     /**
20155      * Load data from the requested source (in this case an in-memory
20156      * data object passed to the constructor), read the data object into
20157      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20158      * process that block using the passed callback.
20159      * @param {Object} params This parameter is not used by the MemoryProxy class.
20160      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20161      * object into a block of Roo.data.Records.
20162      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20163      * The function must be passed <ul>
20164      * <li>The Record block object</li>
20165      * <li>The "arg" argument from the load function</li>
20166      * <li>A boolean success indicator</li>
20167      * </ul>
20168      * @param {Object} scope The scope in which to call the callback
20169      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20170      */
20171     load : function(params, reader, callback, scope, arg){
20172         params = params || {};
20173         var result;
20174         try {
20175             result = reader.readRecords(this.data);
20176         }catch(e){
20177             this.fireEvent("loadexception", this, arg, null, e);
20178             callback.call(scope, null, arg, false);
20179             return;
20180         }
20181         callback.call(scope, result, arg, true);
20182     },
20183     
20184     // private
20185     update : function(params, records){
20186         
20187     }
20188 });/*
20189  * Based on:
20190  * Ext JS Library 1.1.1
20191  * Copyright(c) 2006-2007, Ext JS, LLC.
20192  *
20193  * Originally Released Under LGPL - original licence link has changed is not relivant.
20194  *
20195  * Fork - LGPL
20196  * <script type="text/javascript">
20197  */
20198 /**
20199  * @class Roo.data.HttpProxy
20200  * @extends Roo.data.DataProxy
20201  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20202  * configured to reference a certain URL.<br><br>
20203  * <p>
20204  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20205  * from which the running page was served.<br><br>
20206  * <p>
20207  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20208  * <p>
20209  * Be aware that to enable the browser to parse an XML document, the server must set
20210  * the Content-Type header in the HTTP response to "text/xml".
20211  * @constructor
20212  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20213  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20214  * will be used to make the request.
20215  */
20216 Roo.data.HttpProxy = function(conn){
20217     Roo.data.HttpProxy.superclass.constructor.call(this);
20218     // is conn a conn config or a real conn?
20219     this.conn = conn;
20220     this.useAjax = !conn || !conn.events;
20221   
20222 };
20223
20224 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20225     // thse are take from connection...
20226     
20227     /**
20228      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20229      */
20230     /**
20231      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20232      * extra parameters to each request made by this object. (defaults to undefined)
20233      */
20234     /**
20235      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20236      *  to each request made by this object. (defaults to undefined)
20237      */
20238     /**
20239      * @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)
20240      */
20241     /**
20242      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20243      */
20244      /**
20245      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20246      * @type Boolean
20247      */
20248   
20249
20250     /**
20251      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20252      * @type Boolean
20253      */
20254     /**
20255      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20256      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20257      * a finer-grained basis than the DataProxy events.
20258      */
20259     getConnection : function(){
20260         return this.useAjax ? Roo.Ajax : this.conn;
20261     },
20262
20263     /**
20264      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20265      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20266      * process that block using the passed callback.
20267      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20268      * for the request to the remote server.
20269      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20270      * object into a block of Roo.data.Records.
20271      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20272      * The function must be passed <ul>
20273      * <li>The Record block object</li>
20274      * <li>The "arg" argument from the load function</li>
20275      * <li>A boolean success indicator</li>
20276      * </ul>
20277      * @param {Object} scope The scope in which to call the callback
20278      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20279      */
20280     load : function(params, reader, callback, scope, arg){
20281         if(this.fireEvent("beforeload", this, params) !== false){
20282             var  o = {
20283                 params : params || {},
20284                 request: {
20285                     callback : callback,
20286                     scope : scope,
20287                     arg : arg
20288                 },
20289                 reader: reader,
20290                 callback : this.loadResponse,
20291                 scope: this
20292             };
20293             if(this.useAjax){
20294                 Roo.applyIf(o, this.conn);
20295                 if(this.activeRequest){
20296                     Roo.Ajax.abort(this.activeRequest);
20297                 }
20298                 this.activeRequest = Roo.Ajax.request(o);
20299             }else{
20300                 this.conn.request(o);
20301             }
20302         }else{
20303             callback.call(scope||this, null, arg, false);
20304         }
20305     },
20306
20307     // private
20308     loadResponse : function(o, success, response){
20309         delete this.activeRequest;
20310         if(!success){
20311             this.fireEvent("loadexception", this, o, response);
20312             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20313             return;
20314         }
20315         var result;
20316         try {
20317             result = o.reader.read(response);
20318         }catch(e){
20319             this.fireEvent("loadexception", this, o, response, e);
20320             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20321             return;
20322         }
20323         
20324         this.fireEvent("load", this, o, o.request.arg);
20325         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20326     },
20327
20328     // private
20329     update : function(dataSet){
20330
20331     },
20332
20333     // private
20334     updateResponse : function(dataSet){
20335
20336     }
20337 });/*
20338  * Based on:
20339  * Ext JS Library 1.1.1
20340  * Copyright(c) 2006-2007, Ext JS, LLC.
20341  *
20342  * Originally Released Under LGPL - original licence link has changed is not relivant.
20343  *
20344  * Fork - LGPL
20345  * <script type="text/javascript">
20346  */
20347
20348 /**
20349  * @class Roo.data.ScriptTagProxy
20350  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20351  * other than the originating domain of the running page.<br><br>
20352  * <p>
20353  * <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
20354  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20355  * <p>
20356  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20357  * source code that is used as the source inside a &lt;script> tag.<br><br>
20358  * <p>
20359  * In order for the browser to process the returned data, the server must wrap the data object
20360  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20361  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20362  * depending on whether the callback name was passed:
20363  * <p>
20364  * <pre><code>
20365 boolean scriptTag = false;
20366 String cb = request.getParameter("callback");
20367 if (cb != null) {
20368     scriptTag = true;
20369     response.setContentType("text/javascript");
20370 } else {
20371     response.setContentType("application/x-json");
20372 }
20373 Writer out = response.getWriter();
20374 if (scriptTag) {
20375     out.write(cb + "(");
20376 }
20377 out.print(dataBlock.toJsonString());
20378 if (scriptTag) {
20379     out.write(");");
20380 }
20381 </pre></code>
20382  *
20383  * @constructor
20384  * @param {Object} config A configuration object.
20385  */
20386 Roo.data.ScriptTagProxy = function(config){
20387     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20388     Roo.apply(this, config);
20389     this.head = document.getElementsByTagName("head")[0];
20390 };
20391
20392 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20393
20394 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20395     /**
20396      * @cfg {String} url The URL from which to request the data object.
20397      */
20398     /**
20399      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20400      */
20401     timeout : 30000,
20402     /**
20403      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20404      * the server the name of the callback function set up by the load call to process the returned data object.
20405      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20406      * javascript output which calls this named function passing the data object as its only parameter.
20407      */
20408     callbackParam : "callback",
20409     /**
20410      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20411      * name to the request.
20412      */
20413     nocache : true,
20414
20415     /**
20416      * Load data from the configured URL, read the data object into
20417      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20418      * process that block using the passed callback.
20419      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20420      * for the request to the remote server.
20421      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20422      * object into a block of Roo.data.Records.
20423      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20424      * The function must be passed <ul>
20425      * <li>The Record block object</li>
20426      * <li>The "arg" argument from the load function</li>
20427      * <li>A boolean success indicator</li>
20428      * </ul>
20429      * @param {Object} scope The scope in which to call the callback
20430      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20431      */
20432     load : function(params, reader, callback, scope, arg){
20433         if(this.fireEvent("beforeload", this, params) !== false){
20434
20435             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20436
20437             var url = this.url;
20438             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20439             if(this.nocache){
20440                 url += "&_dc=" + (new Date().getTime());
20441             }
20442             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20443             var trans = {
20444                 id : transId,
20445                 cb : "stcCallback"+transId,
20446                 scriptId : "stcScript"+transId,
20447                 params : params,
20448                 arg : arg,
20449                 url : url,
20450                 callback : callback,
20451                 scope : scope,
20452                 reader : reader
20453             };
20454             var conn = this;
20455
20456             window[trans.cb] = function(o){
20457                 conn.handleResponse(o, trans);
20458             };
20459
20460             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20461
20462             if(this.autoAbort !== false){
20463                 this.abort();
20464             }
20465
20466             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20467
20468             var script = document.createElement("script");
20469             script.setAttribute("src", url);
20470             script.setAttribute("type", "text/javascript");
20471             script.setAttribute("id", trans.scriptId);
20472             this.head.appendChild(script);
20473
20474             this.trans = trans;
20475         }else{
20476             callback.call(scope||this, null, arg, false);
20477         }
20478     },
20479
20480     // private
20481     isLoading : function(){
20482         return this.trans ? true : false;
20483     },
20484
20485     /**
20486      * Abort the current server request.
20487      */
20488     abort : function(){
20489         if(this.isLoading()){
20490             this.destroyTrans(this.trans);
20491         }
20492     },
20493
20494     // private
20495     destroyTrans : function(trans, isLoaded){
20496         this.head.removeChild(document.getElementById(trans.scriptId));
20497         clearTimeout(trans.timeoutId);
20498         if(isLoaded){
20499             window[trans.cb] = undefined;
20500             try{
20501                 delete window[trans.cb];
20502             }catch(e){}
20503         }else{
20504             // if hasn't been loaded, wait for load to remove it to prevent script error
20505             window[trans.cb] = function(){
20506                 window[trans.cb] = undefined;
20507                 try{
20508                     delete window[trans.cb];
20509                 }catch(e){}
20510             };
20511         }
20512     },
20513
20514     // private
20515     handleResponse : function(o, trans){
20516         this.trans = false;
20517         this.destroyTrans(trans, true);
20518         var result;
20519         try {
20520             result = trans.reader.readRecords(o);
20521         }catch(e){
20522             this.fireEvent("loadexception", this, o, trans.arg, e);
20523             trans.callback.call(trans.scope||window, null, trans.arg, false);
20524             return;
20525         }
20526         this.fireEvent("load", this, o, trans.arg);
20527         trans.callback.call(trans.scope||window, result, trans.arg, true);
20528     },
20529
20530     // private
20531     handleFailure : function(trans){
20532         this.trans = false;
20533         this.destroyTrans(trans, false);
20534         this.fireEvent("loadexception", this, null, trans.arg);
20535         trans.callback.call(trans.scope||window, null, trans.arg, false);
20536     }
20537 });/*
20538  * Based on:
20539  * Ext JS Library 1.1.1
20540  * Copyright(c) 2006-2007, Ext JS, LLC.
20541  *
20542  * Originally Released Under LGPL - original licence link has changed is not relivant.
20543  *
20544  * Fork - LGPL
20545  * <script type="text/javascript">
20546  */
20547
20548 /**
20549  * @class Roo.data.JsonReader
20550  * @extends Roo.data.DataReader
20551  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20552  * based on mappings in a provided Roo.data.Record constructor.
20553  * 
20554  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20555  * in the reply previously. 
20556  * 
20557  * <p>
20558  * Example code:
20559  * <pre><code>
20560 var RecordDef = Roo.data.Record.create([
20561     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20562     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20563 ]);
20564 var myReader = new Roo.data.JsonReader({
20565     totalProperty: "results",    // The property which contains the total dataset size (optional)
20566     root: "rows",                // The property which contains an Array of row objects
20567     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20568 }, RecordDef);
20569 </code></pre>
20570  * <p>
20571  * This would consume a JSON file like this:
20572  * <pre><code>
20573 { 'results': 2, 'rows': [
20574     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20575     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20576 }
20577 </code></pre>
20578  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20579  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20580  * paged from the remote server.
20581  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20582  * @cfg {String} root name of the property which contains the Array of row objects.
20583  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20584  * @constructor
20585  * Create a new JsonReader
20586  * @param {Object} meta Metadata configuration options
20587  * @param {Object} recordType Either an Array of field definition objects,
20588  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20589  */
20590 Roo.data.JsonReader = function(meta, recordType){
20591     
20592     meta = meta || {};
20593     // set some defaults:
20594     Roo.applyIf(meta, {
20595         totalProperty: 'total',
20596         successProperty : 'success',
20597         root : 'data',
20598         id : 'id'
20599     });
20600     
20601     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20602 };
20603 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20604     
20605     /**
20606      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20607      * Used by Store query builder to append _requestMeta to params.
20608      * 
20609      */
20610     metaFromRemote : false,
20611     /**
20612      * This method is only used by a DataProxy which has retrieved data from a remote server.
20613      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20614      * @return {Object} data A data block which is used by an Roo.data.Store object as
20615      * a cache of Roo.data.Records.
20616      */
20617     read : function(response){
20618         var json = response.responseText;
20619        
20620         var o = /* eval:var:o */ eval("("+json+")");
20621         if(!o) {
20622             throw {message: "JsonReader.read: Json object not found"};
20623         }
20624         
20625         if(o.metaData){
20626             
20627             delete this.ef;
20628             this.metaFromRemote = true;
20629             this.meta = o.metaData;
20630             this.recordType = Roo.data.Record.create(o.metaData.fields);
20631             this.onMetaChange(this.meta, this.recordType, o);
20632         }
20633         return this.readRecords(o);
20634     },
20635
20636     // private function a store will implement
20637     onMetaChange : function(meta, recordType, o){
20638
20639     },
20640
20641     /**
20642          * @ignore
20643          */
20644     simpleAccess: function(obj, subsc) {
20645         return obj[subsc];
20646     },
20647
20648         /**
20649          * @ignore
20650          */
20651     getJsonAccessor: function(){
20652         var re = /[\[\.]/;
20653         return function(expr) {
20654             try {
20655                 return(re.test(expr))
20656                     ? new Function("obj", "return obj." + expr)
20657                     : function(obj){
20658                         return obj[expr];
20659                     };
20660             } catch(e){}
20661             return Roo.emptyFn;
20662         };
20663     }(),
20664
20665     /**
20666      * Create a data block containing Roo.data.Records from an XML document.
20667      * @param {Object} o An object which contains an Array of row objects in the property specified
20668      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20669      * which contains the total size of the dataset.
20670      * @return {Object} data A data block which is used by an Roo.data.Store object as
20671      * a cache of Roo.data.Records.
20672      */
20673     readRecords : function(o){
20674         /**
20675          * After any data loads, the raw JSON data is available for further custom processing.
20676          * @type Object
20677          */
20678         this.jsonData = o;
20679         var s = this.meta, Record = this.recordType,
20680             f = Record.prototype.fields, fi = f.items, fl = f.length;
20681
20682 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20683         if (!this.ef) {
20684             if(s.totalProperty) {
20685                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20686                 }
20687                 if(s.successProperty) {
20688                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20689                 }
20690                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20691                 if (s.id) {
20692                         var g = this.getJsonAccessor(s.id);
20693                         this.getId = function(rec) {
20694                                 var r = g(rec);
20695                                 return (r === undefined || r === "") ? null : r;
20696                         };
20697                 } else {
20698                         this.getId = function(){return null;};
20699                 }
20700             this.ef = [];
20701             for(var jj = 0; jj < fl; jj++){
20702                 f = fi[jj];
20703                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20704                 this.ef[jj] = this.getJsonAccessor(map);
20705             }
20706         }
20707
20708         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20709         if(s.totalProperty){
20710             var vt = parseInt(this.getTotal(o), 10);
20711             if(!isNaN(vt)){
20712                 totalRecords = vt;
20713             }
20714         }
20715         if(s.successProperty){
20716             var vs = this.getSuccess(o);
20717             if(vs === false || vs === 'false'){
20718                 success = false;
20719             }
20720         }
20721         var records = [];
20722             for(var i = 0; i < c; i++){
20723                     var n = root[i];
20724                 var values = {};
20725                 var id = this.getId(n);
20726                 for(var j = 0; j < fl; j++){
20727                     f = fi[j];
20728                 var v = this.ef[j](n);
20729                 if (!f.convert) {
20730                     Roo.log('missing convert for ' + f.name);
20731                     Roo.log(f);
20732                     continue;
20733                 }
20734                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20735                 }
20736                 var record = new Record(values, id);
20737                 record.json = n;
20738                 records[i] = record;
20739             }
20740             return {
20741                 success : success,
20742                 records : records,
20743                 totalRecords : totalRecords
20744             };
20745     }
20746 });/*
20747  * Based on:
20748  * Ext JS Library 1.1.1
20749  * Copyright(c) 2006-2007, Ext JS, LLC.
20750  *
20751  * Originally Released Under LGPL - original licence link has changed is not relivant.
20752  *
20753  * Fork - LGPL
20754  * <script type="text/javascript">
20755  */
20756
20757 /**
20758  * @class Roo.data.XmlReader
20759  * @extends Roo.data.DataReader
20760  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20761  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20762  * <p>
20763  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20764  * header in the HTTP response must be set to "text/xml".</em>
20765  * <p>
20766  * Example code:
20767  * <pre><code>
20768 var RecordDef = Roo.data.Record.create([
20769    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20770    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20771 ]);
20772 var myReader = new Roo.data.XmlReader({
20773    totalRecords: "results", // The element which contains the total dataset size (optional)
20774    record: "row",           // The repeated element which contains row information
20775    id: "id"                 // The element within the row that provides an ID for the record (optional)
20776 }, RecordDef);
20777 </code></pre>
20778  * <p>
20779  * This would consume an XML file like this:
20780  * <pre><code>
20781 &lt;?xml?>
20782 &lt;dataset>
20783  &lt;results>2&lt;/results>
20784  &lt;row>
20785    &lt;id>1&lt;/id>
20786    &lt;name>Bill&lt;/name>
20787    &lt;occupation>Gardener&lt;/occupation>
20788  &lt;/row>
20789  &lt;row>
20790    &lt;id>2&lt;/id>
20791    &lt;name>Ben&lt;/name>
20792    &lt;occupation>Horticulturalist&lt;/occupation>
20793  &lt;/row>
20794 &lt;/dataset>
20795 </code></pre>
20796  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20797  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20798  * paged from the remote server.
20799  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20800  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20801  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20802  * a record identifier value.
20803  * @constructor
20804  * Create a new XmlReader
20805  * @param {Object} meta Metadata configuration options
20806  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20807  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20808  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20809  */
20810 Roo.data.XmlReader = function(meta, recordType){
20811     meta = meta || {};
20812     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20813 };
20814 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20815     /**
20816      * This method is only used by a DataProxy which has retrieved data from a remote server.
20817          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20818          * to contain a method called 'responseXML' that returns an XML document object.
20819      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20820      * a cache of Roo.data.Records.
20821      */
20822     read : function(response){
20823         var doc = response.responseXML;
20824         if(!doc) {
20825             throw {message: "XmlReader.read: XML Document not available"};
20826         }
20827         return this.readRecords(doc);
20828     },
20829
20830     /**
20831      * Create a data block containing Roo.data.Records from an XML document.
20832          * @param {Object} doc A parsed XML document.
20833      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20834      * a cache of Roo.data.Records.
20835      */
20836     readRecords : function(doc){
20837         /**
20838          * After any data loads/reads, the raw XML Document is available for further custom processing.
20839          * @type XMLDocument
20840          */
20841         this.xmlData = doc;
20842         var root = doc.documentElement || doc;
20843         var q = Roo.DomQuery;
20844         var recordType = this.recordType, fields = recordType.prototype.fields;
20845         var sid = this.meta.id;
20846         var totalRecords = 0, success = true;
20847         if(this.meta.totalRecords){
20848             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20849         }
20850         
20851         if(this.meta.success){
20852             var sv = q.selectValue(this.meta.success, root, true);
20853             success = sv !== false && sv !== 'false';
20854         }
20855         var records = [];
20856         var ns = q.select(this.meta.record, root);
20857         for(var i = 0, len = ns.length; i < len; i++) {
20858                 var n = ns[i];
20859                 var values = {};
20860                 var id = sid ? q.selectValue(sid, n) : undefined;
20861                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20862                     var f = fields.items[j];
20863                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20864                     v = f.convert(v);
20865                     values[f.name] = v;
20866                 }
20867                 var record = new recordType(values, id);
20868                 record.node = n;
20869                 records[records.length] = record;
20870             }
20871
20872             return {
20873                 success : success,
20874                 records : records,
20875                 totalRecords : totalRecords || records.length
20876             };
20877     }
20878 });/*
20879  * Based on:
20880  * Ext JS Library 1.1.1
20881  * Copyright(c) 2006-2007, Ext JS, LLC.
20882  *
20883  * Originally Released Under LGPL - original licence link has changed is not relivant.
20884  *
20885  * Fork - LGPL
20886  * <script type="text/javascript">
20887  */
20888
20889 /**
20890  * @class Roo.data.ArrayReader
20891  * @extends Roo.data.DataReader
20892  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20893  * Each element of that Array represents a row of data fields. The
20894  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20895  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20896  * <p>
20897  * Example code:.
20898  * <pre><code>
20899 var RecordDef = Roo.data.Record.create([
20900     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20901     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20902 ]);
20903 var myReader = new Roo.data.ArrayReader({
20904     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20905 }, RecordDef);
20906 </code></pre>
20907  * <p>
20908  * This would consume an Array like this:
20909  * <pre><code>
20910 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20911   </code></pre>
20912  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20913  * @constructor
20914  * Create a new JsonReader
20915  * @param {Object} meta Metadata configuration options.
20916  * @param {Object} recordType Either an Array of field definition objects
20917  * as specified to {@link Roo.data.Record#create},
20918  * or an {@link Roo.data.Record} object
20919  * created using {@link Roo.data.Record#create}.
20920  */
20921 Roo.data.ArrayReader = function(meta, recordType){
20922     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20923 };
20924
20925 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20926     /**
20927      * Create a data block containing Roo.data.Records from an XML document.
20928      * @param {Object} o An Array of row objects which represents the dataset.
20929      * @return {Object} data A data block which is used by an Roo.data.Store object as
20930      * a cache of Roo.data.Records.
20931      */
20932     readRecords : function(o){
20933         var sid = this.meta ? this.meta.id : null;
20934         var recordType = this.recordType, fields = recordType.prototype.fields;
20935         var records = [];
20936         var root = o;
20937             for(var i = 0; i < root.length; i++){
20938                     var n = root[i];
20939                 var values = {};
20940                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20941                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20942                 var f = fields.items[j];
20943                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20944                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20945                 v = f.convert(v);
20946                 values[f.name] = v;
20947             }
20948                 var record = new recordType(values, id);
20949                 record.json = n;
20950                 records[records.length] = record;
20951             }
20952             return {
20953                 records : records,
20954                 totalRecords : records.length
20955             };
20956     }
20957 });/*
20958  * Based on:
20959  * Ext JS Library 1.1.1
20960  * Copyright(c) 2006-2007, Ext JS, LLC.
20961  *
20962  * Originally Released Under LGPL - original licence link has changed is not relivant.
20963  *
20964  * Fork - LGPL
20965  * <script type="text/javascript">
20966  */
20967
20968
20969 /**
20970  * @class Roo.data.Tree
20971  * @extends Roo.util.Observable
20972  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20973  * in the tree have most standard DOM functionality.
20974  * @constructor
20975  * @param {Node} root (optional) The root node
20976  */
20977 Roo.data.Tree = function(root){
20978    this.nodeHash = {};
20979    /**
20980     * The root node for this tree
20981     * @type Node
20982     */
20983    this.root = null;
20984    if(root){
20985        this.setRootNode(root);
20986    }
20987    this.addEvents({
20988        /**
20989         * @event append
20990         * Fires when a new child node is appended to a node in this tree.
20991         * @param {Tree} tree The owner tree
20992         * @param {Node} parent The parent node
20993         * @param {Node} node The newly appended node
20994         * @param {Number} index The index of the newly appended node
20995         */
20996        "append" : true,
20997        /**
20998         * @event remove
20999         * Fires when a child node is removed from a node in this tree.
21000         * @param {Tree} tree The owner tree
21001         * @param {Node} parent The parent node
21002         * @param {Node} node The child node removed
21003         */
21004        "remove" : true,
21005        /**
21006         * @event move
21007         * Fires when a node is moved to a new location in the tree
21008         * @param {Tree} tree The owner tree
21009         * @param {Node} node The node moved
21010         * @param {Node} oldParent The old parent of this node
21011         * @param {Node} newParent The new parent of this node
21012         * @param {Number} index The index it was moved to
21013         */
21014        "move" : true,
21015        /**
21016         * @event insert
21017         * Fires when a new child node is inserted in a node in this tree.
21018         * @param {Tree} tree The owner tree
21019         * @param {Node} parent The parent node
21020         * @param {Node} node The child node inserted
21021         * @param {Node} refNode The child node the node was inserted before
21022         */
21023        "insert" : true,
21024        /**
21025         * @event beforeappend
21026         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21027         * @param {Tree} tree The owner tree
21028         * @param {Node} parent The parent node
21029         * @param {Node} node The child node to be appended
21030         */
21031        "beforeappend" : true,
21032        /**
21033         * @event beforeremove
21034         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21035         * @param {Tree} tree The owner tree
21036         * @param {Node} parent The parent node
21037         * @param {Node} node The child node to be removed
21038         */
21039        "beforeremove" : true,
21040        /**
21041         * @event beforemove
21042         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21043         * @param {Tree} tree The owner tree
21044         * @param {Node} node The node being moved
21045         * @param {Node} oldParent The parent of the node
21046         * @param {Node} newParent The new parent the node is moving to
21047         * @param {Number} index The index it is being moved to
21048         */
21049        "beforemove" : true,
21050        /**
21051         * @event beforeinsert
21052         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21053         * @param {Tree} tree The owner tree
21054         * @param {Node} parent The parent node
21055         * @param {Node} node The child node to be inserted
21056         * @param {Node} refNode The child node the node is being inserted before
21057         */
21058        "beforeinsert" : true
21059    });
21060
21061     Roo.data.Tree.superclass.constructor.call(this);
21062 };
21063
21064 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21065     pathSeparator: "/",
21066
21067     proxyNodeEvent : function(){
21068         return this.fireEvent.apply(this, arguments);
21069     },
21070
21071     /**
21072      * Returns the root node for this tree.
21073      * @return {Node}
21074      */
21075     getRootNode : function(){
21076         return this.root;
21077     },
21078
21079     /**
21080      * Sets the root node for this tree.
21081      * @param {Node} node
21082      * @return {Node}
21083      */
21084     setRootNode : function(node){
21085         this.root = node;
21086         node.ownerTree = this;
21087         node.isRoot = true;
21088         this.registerNode(node);
21089         return node;
21090     },
21091
21092     /**
21093      * Gets a node in this tree by its id.
21094      * @param {String} id
21095      * @return {Node}
21096      */
21097     getNodeById : function(id){
21098         return this.nodeHash[id];
21099     },
21100
21101     registerNode : function(node){
21102         this.nodeHash[node.id] = node;
21103     },
21104
21105     unregisterNode : function(node){
21106         delete this.nodeHash[node.id];
21107     },
21108
21109     toString : function(){
21110         return "[Tree"+(this.id?" "+this.id:"")+"]";
21111     }
21112 });
21113
21114 /**
21115  * @class Roo.data.Node
21116  * @extends Roo.util.Observable
21117  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21118  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21119  * @constructor
21120  * @param {Object} attributes The attributes/config for the node
21121  */
21122 Roo.data.Node = function(attributes){
21123     /**
21124      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21125      * @type {Object}
21126      */
21127     this.attributes = attributes || {};
21128     this.leaf = this.attributes.leaf;
21129     /**
21130      * The node id. @type String
21131      */
21132     this.id = this.attributes.id;
21133     if(!this.id){
21134         this.id = Roo.id(null, "ynode-");
21135         this.attributes.id = this.id;
21136     }
21137      
21138     
21139     /**
21140      * All child nodes of this node. @type Array
21141      */
21142     this.childNodes = [];
21143     if(!this.childNodes.indexOf){ // indexOf is a must
21144         this.childNodes.indexOf = function(o){
21145             for(var i = 0, len = this.length; i < len; i++){
21146                 if(this[i] == o) {
21147                     return i;
21148                 }
21149             }
21150             return -1;
21151         };
21152     }
21153     /**
21154      * The parent node for this node. @type Node
21155      */
21156     this.parentNode = null;
21157     /**
21158      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21159      */
21160     this.firstChild = null;
21161     /**
21162      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21163      */
21164     this.lastChild = null;
21165     /**
21166      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21167      */
21168     this.previousSibling = null;
21169     /**
21170      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21171      */
21172     this.nextSibling = null;
21173
21174     this.addEvents({
21175        /**
21176         * @event append
21177         * Fires when a new child node is appended
21178         * @param {Tree} tree The owner tree
21179         * @param {Node} this This node
21180         * @param {Node} node The newly appended node
21181         * @param {Number} index The index of the newly appended node
21182         */
21183        "append" : true,
21184        /**
21185         * @event remove
21186         * Fires when a child node is removed
21187         * @param {Tree} tree The owner tree
21188         * @param {Node} this This node
21189         * @param {Node} node The removed node
21190         */
21191        "remove" : true,
21192        /**
21193         * @event move
21194         * Fires when this node is moved to a new location in the tree
21195         * @param {Tree} tree The owner tree
21196         * @param {Node} this This node
21197         * @param {Node} oldParent The old parent of this node
21198         * @param {Node} newParent The new parent of this node
21199         * @param {Number} index The index it was moved to
21200         */
21201        "move" : true,
21202        /**
21203         * @event insert
21204         * Fires when a new child node is inserted.
21205         * @param {Tree} tree The owner tree
21206         * @param {Node} this This node
21207         * @param {Node} node The child node inserted
21208         * @param {Node} refNode The child node the node was inserted before
21209         */
21210        "insert" : true,
21211        /**
21212         * @event beforeappend
21213         * Fires before a new child is appended, return false to cancel the append.
21214         * @param {Tree} tree The owner tree
21215         * @param {Node} this This node
21216         * @param {Node} node The child node to be appended
21217         */
21218        "beforeappend" : true,
21219        /**
21220         * @event beforeremove
21221         * Fires before a child is removed, return false to cancel the remove.
21222         * @param {Tree} tree The owner tree
21223         * @param {Node} this This node
21224         * @param {Node} node The child node to be removed
21225         */
21226        "beforeremove" : true,
21227        /**
21228         * @event beforemove
21229         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21230         * @param {Tree} tree The owner tree
21231         * @param {Node} this This node
21232         * @param {Node} oldParent The parent of this node
21233         * @param {Node} newParent The new parent this node is moving to
21234         * @param {Number} index The index it is being moved to
21235         */
21236        "beforemove" : true,
21237        /**
21238         * @event beforeinsert
21239         * Fires before a new child is inserted, return false to cancel the insert.
21240         * @param {Tree} tree The owner tree
21241         * @param {Node} this This node
21242         * @param {Node} node The child node to be inserted
21243         * @param {Node} refNode The child node the node is being inserted before
21244         */
21245        "beforeinsert" : true
21246    });
21247     this.listeners = this.attributes.listeners;
21248     Roo.data.Node.superclass.constructor.call(this);
21249 };
21250
21251 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21252     fireEvent : function(evtName){
21253         // first do standard event for this node
21254         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21255             return false;
21256         }
21257         // then bubble it up to the tree if the event wasn't cancelled
21258         var ot = this.getOwnerTree();
21259         if(ot){
21260             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21261                 return false;
21262             }
21263         }
21264         return true;
21265     },
21266
21267     /**
21268      * Returns true if this node is a leaf
21269      * @return {Boolean}
21270      */
21271     isLeaf : function(){
21272         return this.leaf === true;
21273     },
21274
21275     // private
21276     setFirstChild : function(node){
21277         this.firstChild = node;
21278     },
21279
21280     //private
21281     setLastChild : function(node){
21282         this.lastChild = node;
21283     },
21284
21285
21286     /**
21287      * Returns true if this node is the last child of its parent
21288      * @return {Boolean}
21289      */
21290     isLast : function(){
21291        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21292     },
21293
21294     /**
21295      * Returns true if this node is the first child of its parent
21296      * @return {Boolean}
21297      */
21298     isFirst : function(){
21299        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21300     },
21301
21302     hasChildNodes : function(){
21303         return !this.isLeaf() && this.childNodes.length > 0;
21304     },
21305
21306     /**
21307      * Insert node(s) as the last child node of this node.
21308      * @param {Node/Array} node The node or Array of nodes to append
21309      * @return {Node} The appended node if single append, or null if an array was passed
21310      */
21311     appendChild : function(node){
21312         var multi = false;
21313         if(node instanceof Array){
21314             multi = node;
21315         }else if(arguments.length > 1){
21316             multi = arguments;
21317         }
21318         // if passed an array or multiple args do them one by one
21319         if(multi){
21320             for(var i = 0, len = multi.length; i < len; i++) {
21321                 this.appendChild(multi[i]);
21322             }
21323         }else{
21324             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21325                 return false;
21326             }
21327             var index = this.childNodes.length;
21328             var oldParent = node.parentNode;
21329             // it's a move, make sure we move it cleanly
21330             if(oldParent){
21331                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21332                     return false;
21333                 }
21334                 oldParent.removeChild(node);
21335             }
21336             index = this.childNodes.length;
21337             if(index == 0){
21338                 this.setFirstChild(node);
21339             }
21340             this.childNodes.push(node);
21341             node.parentNode = this;
21342             var ps = this.childNodes[index-1];
21343             if(ps){
21344                 node.previousSibling = ps;
21345                 ps.nextSibling = node;
21346             }else{
21347                 node.previousSibling = null;
21348             }
21349             node.nextSibling = null;
21350             this.setLastChild(node);
21351             node.setOwnerTree(this.getOwnerTree());
21352             this.fireEvent("append", this.ownerTree, this, node, index);
21353             if(oldParent){
21354                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21355             }
21356             return node;
21357         }
21358     },
21359
21360     /**
21361      * Removes a child node from this node.
21362      * @param {Node} node The node to remove
21363      * @return {Node} The removed node
21364      */
21365     removeChild : function(node){
21366         var index = this.childNodes.indexOf(node);
21367         if(index == -1){
21368             return false;
21369         }
21370         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21371             return false;
21372         }
21373
21374         // remove it from childNodes collection
21375         this.childNodes.splice(index, 1);
21376
21377         // update siblings
21378         if(node.previousSibling){
21379             node.previousSibling.nextSibling = node.nextSibling;
21380         }
21381         if(node.nextSibling){
21382             node.nextSibling.previousSibling = node.previousSibling;
21383         }
21384
21385         // update child refs
21386         if(this.firstChild == node){
21387             this.setFirstChild(node.nextSibling);
21388         }
21389         if(this.lastChild == node){
21390             this.setLastChild(node.previousSibling);
21391         }
21392
21393         node.setOwnerTree(null);
21394         // clear any references from the node
21395         node.parentNode = null;
21396         node.previousSibling = null;
21397         node.nextSibling = null;
21398         this.fireEvent("remove", this.ownerTree, this, node);
21399         return node;
21400     },
21401
21402     /**
21403      * Inserts the first node before the second node in this nodes childNodes collection.
21404      * @param {Node} node The node to insert
21405      * @param {Node} refNode The node to insert before (if null the node is appended)
21406      * @return {Node} The inserted node
21407      */
21408     insertBefore : function(node, refNode){
21409         if(!refNode){ // like standard Dom, refNode can be null for append
21410             return this.appendChild(node);
21411         }
21412         // nothing to do
21413         if(node == refNode){
21414             return false;
21415         }
21416
21417         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21418             return false;
21419         }
21420         var index = this.childNodes.indexOf(refNode);
21421         var oldParent = node.parentNode;
21422         var refIndex = index;
21423
21424         // when moving internally, indexes will change after remove
21425         if(oldParent == this && this.childNodes.indexOf(node) < index){
21426             refIndex--;
21427         }
21428
21429         // it's a move, make sure we move it cleanly
21430         if(oldParent){
21431             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21432                 return false;
21433             }
21434             oldParent.removeChild(node);
21435         }
21436         if(refIndex == 0){
21437             this.setFirstChild(node);
21438         }
21439         this.childNodes.splice(refIndex, 0, node);
21440         node.parentNode = this;
21441         var ps = this.childNodes[refIndex-1];
21442         if(ps){
21443             node.previousSibling = ps;
21444             ps.nextSibling = node;
21445         }else{
21446             node.previousSibling = null;
21447         }
21448         node.nextSibling = refNode;
21449         refNode.previousSibling = node;
21450         node.setOwnerTree(this.getOwnerTree());
21451         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21452         if(oldParent){
21453             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21454         }
21455         return node;
21456     },
21457
21458     /**
21459      * Returns the child node at the specified index.
21460      * @param {Number} index
21461      * @return {Node}
21462      */
21463     item : function(index){
21464         return this.childNodes[index];
21465     },
21466
21467     /**
21468      * Replaces one child node in this node with another.
21469      * @param {Node} newChild The replacement node
21470      * @param {Node} oldChild The node to replace
21471      * @return {Node} The replaced node
21472      */
21473     replaceChild : function(newChild, oldChild){
21474         this.insertBefore(newChild, oldChild);
21475         this.removeChild(oldChild);
21476         return oldChild;
21477     },
21478
21479     /**
21480      * Returns the index of a child node
21481      * @param {Node} node
21482      * @return {Number} The index of the node or -1 if it was not found
21483      */
21484     indexOf : function(child){
21485         return this.childNodes.indexOf(child);
21486     },
21487
21488     /**
21489      * Returns the tree this node is in.
21490      * @return {Tree}
21491      */
21492     getOwnerTree : function(){
21493         // if it doesn't have one, look for one
21494         if(!this.ownerTree){
21495             var p = this;
21496             while(p){
21497                 if(p.ownerTree){
21498                     this.ownerTree = p.ownerTree;
21499                     break;
21500                 }
21501                 p = p.parentNode;
21502             }
21503         }
21504         return this.ownerTree;
21505     },
21506
21507     /**
21508      * Returns depth of this node (the root node has a depth of 0)
21509      * @return {Number}
21510      */
21511     getDepth : function(){
21512         var depth = 0;
21513         var p = this;
21514         while(p.parentNode){
21515             ++depth;
21516             p = p.parentNode;
21517         }
21518         return depth;
21519     },
21520
21521     // private
21522     setOwnerTree : function(tree){
21523         // if it's move, we need to update everyone
21524         if(tree != this.ownerTree){
21525             if(this.ownerTree){
21526                 this.ownerTree.unregisterNode(this);
21527             }
21528             this.ownerTree = tree;
21529             var cs = this.childNodes;
21530             for(var i = 0, len = cs.length; i < len; i++) {
21531                 cs[i].setOwnerTree(tree);
21532             }
21533             if(tree){
21534                 tree.registerNode(this);
21535             }
21536         }
21537     },
21538
21539     /**
21540      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21541      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21542      * @return {String} The path
21543      */
21544     getPath : function(attr){
21545         attr = attr || "id";
21546         var p = this.parentNode;
21547         var b = [this.attributes[attr]];
21548         while(p){
21549             b.unshift(p.attributes[attr]);
21550             p = p.parentNode;
21551         }
21552         var sep = this.getOwnerTree().pathSeparator;
21553         return sep + b.join(sep);
21554     },
21555
21556     /**
21557      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21558      * function call will be the scope provided or the current node. The arguments to the function
21559      * will be the args provided or the current node. If the function returns false at any point,
21560      * the bubble is stopped.
21561      * @param {Function} fn The function to call
21562      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21563      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21564      */
21565     bubble : function(fn, scope, args){
21566         var p = this;
21567         while(p){
21568             if(fn.call(scope || p, args || p) === false){
21569                 break;
21570             }
21571             p = p.parentNode;
21572         }
21573     },
21574
21575     /**
21576      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21577      * function call will be the scope provided or the current node. The arguments to the function
21578      * will be the args provided or the current node. If the function returns false at any point,
21579      * the cascade is stopped on that branch.
21580      * @param {Function} fn The function to call
21581      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21582      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21583      */
21584     cascade : function(fn, scope, args){
21585         if(fn.call(scope || this, args || this) !== false){
21586             var cs = this.childNodes;
21587             for(var i = 0, len = cs.length; i < len; i++) {
21588                 cs[i].cascade(fn, scope, args);
21589             }
21590         }
21591     },
21592
21593     /**
21594      * Interates the child nodes of 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 iteration stops.
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     eachChild : function(fn, scope, args){
21603         var cs = this.childNodes;
21604         for(var i = 0, len = cs.length; i < len; i++) {
21605                 if(fn.call(scope || this, args || cs[i]) === false){
21606                     break;
21607                 }
21608         }
21609     },
21610
21611     /**
21612      * Finds the first child that has the attribute with the specified value.
21613      * @param {String} attribute The attribute name
21614      * @param {Mixed} value The value to search for
21615      * @return {Node} The found child or null if none was found
21616      */
21617     findChild : function(attribute, value){
21618         var cs = this.childNodes;
21619         for(var i = 0, len = cs.length; i < len; i++) {
21620                 if(cs[i].attributes[attribute] == value){
21621                     return cs[i];
21622                 }
21623         }
21624         return null;
21625     },
21626
21627     /**
21628      * Finds the first child by a custom function. The child matches if the function passed
21629      * returns true.
21630      * @param {Function} fn
21631      * @param {Object} scope (optional)
21632      * @return {Node} The found child or null if none was found
21633      */
21634     findChildBy : function(fn, scope){
21635         var cs = this.childNodes;
21636         for(var i = 0, len = cs.length; i < len; i++) {
21637                 if(fn.call(scope||cs[i], cs[i]) === true){
21638                     return cs[i];
21639                 }
21640         }
21641         return null;
21642     },
21643
21644     /**
21645      * Sorts this nodes children using the supplied sort function
21646      * @param {Function} fn
21647      * @param {Object} scope (optional)
21648      */
21649     sort : function(fn, scope){
21650         var cs = this.childNodes;
21651         var len = cs.length;
21652         if(len > 0){
21653             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21654             cs.sort(sortFn);
21655             for(var i = 0; i < len; i++){
21656                 var n = cs[i];
21657                 n.previousSibling = cs[i-1];
21658                 n.nextSibling = cs[i+1];
21659                 if(i == 0){
21660                     this.setFirstChild(n);
21661                 }
21662                 if(i == len-1){
21663                     this.setLastChild(n);
21664                 }
21665             }
21666         }
21667     },
21668
21669     /**
21670      * Returns true if this node is an ancestor (at any point) of the passed node.
21671      * @param {Node} node
21672      * @return {Boolean}
21673      */
21674     contains : function(node){
21675         return node.isAncestor(this);
21676     },
21677
21678     /**
21679      * Returns true if the passed node is an ancestor (at any point) of this node.
21680      * @param {Node} node
21681      * @return {Boolean}
21682      */
21683     isAncestor : function(node){
21684         var p = this.parentNode;
21685         while(p){
21686             if(p == node){
21687                 return true;
21688             }
21689             p = p.parentNode;
21690         }
21691         return false;
21692     },
21693
21694     toString : function(){
21695         return "[Node"+(this.id?" "+this.id:"")+"]";
21696     }
21697 });/*
21698  * Based on:
21699  * Ext JS Library 1.1.1
21700  * Copyright(c) 2006-2007, Ext JS, LLC.
21701  *
21702  * Originally Released Under LGPL - original licence link has changed is not relivant.
21703  *
21704  * Fork - LGPL
21705  * <script type="text/javascript">
21706  */
21707  
21708
21709 /**
21710  * @class Roo.ComponentMgr
21711  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21712  * @singleton
21713  */
21714 Roo.ComponentMgr = function(){
21715     var all = new Roo.util.MixedCollection();
21716
21717     return {
21718         /**
21719          * Registers a component.
21720          * @param {Roo.Component} c The component
21721          */
21722         register : function(c){
21723             all.add(c);
21724         },
21725
21726         /**
21727          * Unregisters a component.
21728          * @param {Roo.Component} c The component
21729          */
21730         unregister : function(c){
21731             all.remove(c);
21732         },
21733
21734         /**
21735          * Returns a component by id
21736          * @param {String} id The component id
21737          */
21738         get : function(id){
21739             return all.get(id);
21740         },
21741
21742         /**
21743          * Registers a function that will be called when a specified component is added to ComponentMgr
21744          * @param {String} id The component id
21745          * @param {Funtction} fn The callback function
21746          * @param {Object} scope The scope of the callback
21747          */
21748         onAvailable : function(id, fn, scope){
21749             all.on("add", function(index, o){
21750                 if(o.id == id){
21751                     fn.call(scope || o, o);
21752                     all.un("add", fn, scope);
21753                 }
21754             });
21755         }
21756     };
21757 }();/*
21758  * Based on:
21759  * Ext JS Library 1.1.1
21760  * Copyright(c) 2006-2007, Ext JS, LLC.
21761  *
21762  * Originally Released Under LGPL - original licence link has changed is not relivant.
21763  *
21764  * Fork - LGPL
21765  * <script type="text/javascript">
21766  */
21767  
21768 /**
21769  * @class Roo.Component
21770  * @extends Roo.util.Observable
21771  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21772  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21773  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21774  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21775  * All visual components (widgets) that require rendering into a layout should subclass Component.
21776  * @constructor
21777  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21778  * 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
21779  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21780  */
21781 Roo.Component = function(config){
21782     config = config || {};
21783     if(config.tagName || config.dom || typeof config == "string"){ // element object
21784         config = {el: config, id: config.id || config};
21785     }
21786     this.initialConfig = config;
21787
21788     Roo.apply(this, config);
21789     this.addEvents({
21790         /**
21791          * @event disable
21792          * Fires after the component is disabled.
21793              * @param {Roo.Component} this
21794              */
21795         disable : true,
21796         /**
21797          * @event enable
21798          * Fires after the component is enabled.
21799              * @param {Roo.Component} this
21800              */
21801         enable : true,
21802         /**
21803          * @event beforeshow
21804          * Fires before the component is shown.  Return false to stop the show.
21805              * @param {Roo.Component} this
21806              */
21807         beforeshow : true,
21808         /**
21809          * @event show
21810          * Fires after the component is shown.
21811              * @param {Roo.Component} this
21812              */
21813         show : true,
21814         /**
21815          * @event beforehide
21816          * Fires before the component is hidden. Return false to stop the hide.
21817              * @param {Roo.Component} this
21818              */
21819         beforehide : true,
21820         /**
21821          * @event hide
21822          * Fires after the component is hidden.
21823              * @param {Roo.Component} this
21824              */
21825         hide : true,
21826         /**
21827          * @event beforerender
21828          * Fires before the component is rendered. Return false to stop the render.
21829              * @param {Roo.Component} this
21830              */
21831         beforerender : true,
21832         /**
21833          * @event render
21834          * Fires after the component is rendered.
21835              * @param {Roo.Component} this
21836              */
21837         render : true,
21838         /**
21839          * @event beforedestroy
21840          * Fires before the component is destroyed. Return false to stop the destroy.
21841              * @param {Roo.Component} this
21842              */
21843         beforedestroy : true,
21844         /**
21845          * @event destroy
21846          * Fires after the component is destroyed.
21847              * @param {Roo.Component} this
21848              */
21849         destroy : true
21850     });
21851     if(!this.id){
21852         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21853     }
21854     Roo.ComponentMgr.register(this);
21855     Roo.Component.superclass.constructor.call(this);
21856     this.initComponent();
21857     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21858         this.render(this.renderTo);
21859         delete this.renderTo;
21860     }
21861 };
21862
21863 /** @private */
21864 Roo.Component.AUTO_ID = 1000;
21865
21866 Roo.extend(Roo.Component, Roo.util.Observable, {
21867     /**
21868      * @scope Roo.Component.prototype
21869      * @type {Boolean}
21870      * true if this component is hidden. Read-only.
21871      */
21872     hidden : false,
21873     /**
21874      * @type {Boolean}
21875      * true if this component is disabled. Read-only.
21876      */
21877     disabled : false,
21878     /**
21879      * @type {Boolean}
21880      * true if this component has been rendered. Read-only.
21881      */
21882     rendered : false,
21883     
21884     /** @cfg {String} disableClass
21885      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21886      */
21887     disabledClass : "x-item-disabled",
21888         /** @cfg {Boolean} allowDomMove
21889          * Whether the component can move the Dom node when rendering (defaults to true).
21890          */
21891     allowDomMove : true,
21892     /** @cfg {String} hideMode
21893      * How this component should hidden. Supported values are
21894      * "visibility" (css visibility), "offsets" (negative offset position) and
21895      * "display" (css display) - defaults to "display".
21896      */
21897     hideMode: 'display',
21898
21899     /** @private */
21900     ctype : "Roo.Component",
21901
21902     /**
21903      * @cfg {String} actionMode 
21904      * which property holds the element that used for  hide() / show() / disable() / enable()
21905      * default is 'el' 
21906      */
21907     actionMode : "el",
21908
21909     /** @private */
21910     getActionEl : function(){
21911         return this[this.actionMode];
21912     },
21913
21914     initComponent : Roo.emptyFn,
21915     /**
21916      * If this is a lazy rendering component, render it to its container element.
21917      * @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.
21918      */
21919     render : function(container, position){
21920         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21921             if(!container && this.el){
21922                 this.el = Roo.get(this.el);
21923                 container = this.el.dom.parentNode;
21924                 this.allowDomMove = false;
21925             }
21926             this.container = Roo.get(container);
21927             this.rendered = true;
21928             if(position !== undefined){
21929                 if(typeof position == 'number'){
21930                     position = this.container.dom.childNodes[position];
21931                 }else{
21932                     position = Roo.getDom(position);
21933                 }
21934             }
21935             this.onRender(this.container, position || null);
21936             if(this.cls){
21937                 this.el.addClass(this.cls);
21938                 delete this.cls;
21939             }
21940             if(this.style){
21941                 this.el.applyStyles(this.style);
21942                 delete this.style;
21943             }
21944             this.fireEvent("render", this);
21945             this.afterRender(this.container);
21946             if(this.hidden){
21947                 this.hide();
21948             }
21949             if(this.disabled){
21950                 this.disable();
21951             }
21952         }
21953         return this;
21954     },
21955
21956     /** @private */
21957     // default function is not really useful
21958     onRender : function(ct, position){
21959         if(this.el){
21960             this.el = Roo.get(this.el);
21961             if(this.allowDomMove !== false){
21962                 ct.dom.insertBefore(this.el.dom, position);
21963             }
21964         }
21965     },
21966
21967     /** @private */
21968     getAutoCreate : function(){
21969         var cfg = typeof this.autoCreate == "object" ?
21970                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21971         if(this.id && !cfg.id){
21972             cfg.id = this.id;
21973         }
21974         return cfg;
21975     },
21976
21977     /** @private */
21978     afterRender : Roo.emptyFn,
21979
21980     /**
21981      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21982      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21983      */
21984     destroy : function(){
21985         if(this.fireEvent("beforedestroy", this) !== false){
21986             this.purgeListeners();
21987             this.beforeDestroy();
21988             if(this.rendered){
21989                 this.el.removeAllListeners();
21990                 this.el.remove();
21991                 if(this.actionMode == "container"){
21992                     this.container.remove();
21993                 }
21994             }
21995             this.onDestroy();
21996             Roo.ComponentMgr.unregister(this);
21997             this.fireEvent("destroy", this);
21998         }
21999     },
22000
22001         /** @private */
22002     beforeDestroy : function(){
22003
22004     },
22005
22006         /** @private */
22007         onDestroy : function(){
22008
22009     },
22010
22011     /**
22012      * Returns the underlying {@link Roo.Element}.
22013      * @return {Roo.Element} The element
22014      */
22015     getEl : function(){
22016         return this.el;
22017     },
22018
22019     /**
22020      * Returns the id of this component.
22021      * @return {String}
22022      */
22023     getId : function(){
22024         return this.id;
22025     },
22026
22027     /**
22028      * Try to focus this component.
22029      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22030      * @return {Roo.Component} this
22031      */
22032     focus : function(selectText){
22033         if(this.rendered){
22034             this.el.focus();
22035             if(selectText === true){
22036                 this.el.dom.select();
22037             }
22038         }
22039         return this;
22040     },
22041
22042     /** @private */
22043     blur : function(){
22044         if(this.rendered){
22045             this.el.blur();
22046         }
22047         return this;
22048     },
22049
22050     /**
22051      * Disable this component.
22052      * @return {Roo.Component} this
22053      */
22054     disable : function(){
22055         if(this.rendered){
22056             this.onDisable();
22057         }
22058         this.disabled = true;
22059         this.fireEvent("disable", this);
22060         return this;
22061     },
22062
22063         // private
22064     onDisable : function(){
22065         this.getActionEl().addClass(this.disabledClass);
22066         this.el.dom.disabled = true;
22067     },
22068
22069     /**
22070      * Enable this component.
22071      * @return {Roo.Component} this
22072      */
22073     enable : function(){
22074         if(this.rendered){
22075             this.onEnable();
22076         }
22077         this.disabled = false;
22078         this.fireEvent("enable", this);
22079         return this;
22080     },
22081
22082         // private
22083     onEnable : function(){
22084         this.getActionEl().removeClass(this.disabledClass);
22085         this.el.dom.disabled = false;
22086     },
22087
22088     /**
22089      * Convenience function for setting disabled/enabled by boolean.
22090      * @param {Boolean} disabled
22091      */
22092     setDisabled : function(disabled){
22093         this[disabled ? "disable" : "enable"]();
22094     },
22095
22096     /**
22097      * Show this component.
22098      * @return {Roo.Component} this
22099      */
22100     show: function(){
22101         if(this.fireEvent("beforeshow", this) !== false){
22102             this.hidden = false;
22103             if(this.rendered){
22104                 this.onShow();
22105             }
22106             this.fireEvent("show", this);
22107         }
22108         return this;
22109     },
22110
22111     // private
22112     onShow : function(){
22113         var ae = this.getActionEl();
22114         if(this.hideMode == 'visibility'){
22115             ae.dom.style.visibility = "visible";
22116         }else if(this.hideMode == 'offsets'){
22117             ae.removeClass('x-hidden');
22118         }else{
22119             ae.dom.style.display = "";
22120         }
22121     },
22122
22123     /**
22124      * Hide this component.
22125      * @return {Roo.Component} this
22126      */
22127     hide: function(){
22128         if(this.fireEvent("beforehide", this) !== false){
22129             this.hidden = true;
22130             if(this.rendered){
22131                 this.onHide();
22132             }
22133             this.fireEvent("hide", this);
22134         }
22135         return this;
22136     },
22137
22138     // private
22139     onHide : function(){
22140         var ae = this.getActionEl();
22141         if(this.hideMode == 'visibility'){
22142             ae.dom.style.visibility = "hidden";
22143         }else if(this.hideMode == 'offsets'){
22144             ae.addClass('x-hidden');
22145         }else{
22146             ae.dom.style.display = "none";
22147         }
22148     },
22149
22150     /**
22151      * Convenience function to hide or show this component by boolean.
22152      * @param {Boolean} visible True to show, false to hide
22153      * @return {Roo.Component} this
22154      */
22155     setVisible: function(visible){
22156         if(visible) {
22157             this.show();
22158         }else{
22159             this.hide();
22160         }
22161         return this;
22162     },
22163
22164     /**
22165      * Returns true if this component is visible.
22166      */
22167     isVisible : function(){
22168         return this.getActionEl().isVisible();
22169     },
22170
22171     cloneConfig : function(overrides){
22172         overrides = overrides || {};
22173         var id = overrides.id || Roo.id();
22174         var cfg = Roo.applyIf(overrides, this.initialConfig);
22175         cfg.id = id; // prevent dup id
22176         return new this.constructor(cfg);
22177     }
22178 });/*
22179  * Based on:
22180  * Ext JS Library 1.1.1
22181  * Copyright(c) 2006-2007, Ext JS, LLC.
22182  *
22183  * Originally Released Under LGPL - original licence link has changed is not relivant.
22184  *
22185  * Fork - LGPL
22186  * <script type="text/javascript">
22187  */
22188  (function(){ 
22189 /**
22190  * @class Roo.Layer
22191  * @extends Roo.Element
22192  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22193  * automatic maintaining of shadow/shim positions.
22194  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22195  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22196  * you can pass a string with a CSS class name. False turns off the shadow.
22197  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22198  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22199  * @cfg {String} cls CSS class to add to the element
22200  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22201  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22202  * @constructor
22203  * @param {Object} config An object with config options.
22204  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22205  */
22206
22207 Roo.Layer = function(config, existingEl){
22208     config = config || {};
22209     var dh = Roo.DomHelper;
22210     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22211     if(existingEl){
22212         this.dom = Roo.getDom(existingEl);
22213     }
22214     if(!this.dom){
22215         var o = config.dh || {tag: "div", cls: "x-layer"};
22216         this.dom = dh.append(pel, o);
22217     }
22218     if(config.cls){
22219         this.addClass(config.cls);
22220     }
22221     this.constrain = config.constrain !== false;
22222     this.visibilityMode = Roo.Element.VISIBILITY;
22223     if(config.id){
22224         this.id = this.dom.id = config.id;
22225     }else{
22226         this.id = Roo.id(this.dom);
22227     }
22228     this.zindex = config.zindex || this.getZIndex();
22229     this.position("absolute", this.zindex);
22230     if(config.shadow){
22231         this.shadowOffset = config.shadowOffset || 4;
22232         this.shadow = new Roo.Shadow({
22233             offset : this.shadowOffset,
22234             mode : config.shadow
22235         });
22236     }else{
22237         this.shadowOffset = 0;
22238     }
22239     this.useShim = config.shim !== false && Roo.useShims;
22240     this.useDisplay = config.useDisplay;
22241     this.hide();
22242 };
22243
22244 var supr = Roo.Element.prototype;
22245
22246 // shims are shared among layer to keep from having 100 iframes
22247 var shims = [];
22248
22249 Roo.extend(Roo.Layer, Roo.Element, {
22250
22251     getZIndex : function(){
22252         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22253     },
22254
22255     getShim : function(){
22256         if(!this.useShim){
22257             return null;
22258         }
22259         if(this.shim){
22260             return this.shim;
22261         }
22262         var shim = shims.shift();
22263         if(!shim){
22264             shim = this.createShim();
22265             shim.enableDisplayMode('block');
22266             shim.dom.style.display = 'none';
22267             shim.dom.style.visibility = 'visible';
22268         }
22269         var pn = this.dom.parentNode;
22270         if(shim.dom.parentNode != pn){
22271             pn.insertBefore(shim.dom, this.dom);
22272         }
22273         shim.setStyle('z-index', this.getZIndex()-2);
22274         this.shim = shim;
22275         return shim;
22276     },
22277
22278     hideShim : function(){
22279         if(this.shim){
22280             this.shim.setDisplayed(false);
22281             shims.push(this.shim);
22282             delete this.shim;
22283         }
22284     },
22285
22286     disableShadow : function(){
22287         if(this.shadow){
22288             this.shadowDisabled = true;
22289             this.shadow.hide();
22290             this.lastShadowOffset = this.shadowOffset;
22291             this.shadowOffset = 0;
22292         }
22293     },
22294
22295     enableShadow : function(show){
22296         if(this.shadow){
22297             this.shadowDisabled = false;
22298             this.shadowOffset = this.lastShadowOffset;
22299             delete this.lastShadowOffset;
22300             if(show){
22301                 this.sync(true);
22302             }
22303         }
22304     },
22305
22306     // private
22307     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22308     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22309     sync : function(doShow){
22310         var sw = this.shadow;
22311         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22312             var sh = this.getShim();
22313
22314             var w = this.getWidth(),
22315                 h = this.getHeight();
22316
22317             var l = this.getLeft(true),
22318                 t = this.getTop(true);
22319
22320             if(sw && !this.shadowDisabled){
22321                 if(doShow && !sw.isVisible()){
22322                     sw.show(this);
22323                 }else{
22324                     sw.realign(l, t, w, h);
22325                 }
22326                 if(sh){
22327                     if(doShow){
22328                        sh.show();
22329                     }
22330                     // fit the shim behind the shadow, so it is shimmed too
22331                     var a = sw.adjusts, s = sh.dom.style;
22332                     s.left = (Math.min(l, l+a.l))+"px";
22333                     s.top = (Math.min(t, t+a.t))+"px";
22334                     s.width = (w+a.w)+"px";
22335                     s.height = (h+a.h)+"px";
22336                 }
22337             }else if(sh){
22338                 if(doShow){
22339                    sh.show();
22340                 }
22341                 sh.setSize(w, h);
22342                 sh.setLeftTop(l, t);
22343             }
22344             
22345         }
22346     },
22347
22348     // private
22349     destroy : function(){
22350         this.hideShim();
22351         if(this.shadow){
22352             this.shadow.hide();
22353         }
22354         this.removeAllListeners();
22355         var pn = this.dom.parentNode;
22356         if(pn){
22357             pn.removeChild(this.dom);
22358         }
22359         Roo.Element.uncache(this.id);
22360     },
22361
22362     remove : function(){
22363         this.destroy();
22364     },
22365
22366     // private
22367     beginUpdate : function(){
22368         this.updating = true;
22369     },
22370
22371     // private
22372     endUpdate : function(){
22373         this.updating = false;
22374         this.sync(true);
22375     },
22376
22377     // private
22378     hideUnders : function(negOffset){
22379         if(this.shadow){
22380             this.shadow.hide();
22381         }
22382         this.hideShim();
22383     },
22384
22385     // private
22386     constrainXY : function(){
22387         if(this.constrain){
22388             var vw = Roo.lib.Dom.getViewWidth(),
22389                 vh = Roo.lib.Dom.getViewHeight();
22390             var s = Roo.get(document).getScroll();
22391
22392             var xy = this.getXY();
22393             var x = xy[0], y = xy[1];   
22394             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22395             // only move it if it needs it
22396             var moved = false;
22397             // first validate right/bottom
22398             if((x + w) > vw+s.left){
22399                 x = vw - w - this.shadowOffset;
22400                 moved = true;
22401             }
22402             if((y + h) > vh+s.top){
22403                 y = vh - h - this.shadowOffset;
22404                 moved = true;
22405             }
22406             // then make sure top/left isn't negative
22407             if(x < s.left){
22408                 x = s.left;
22409                 moved = true;
22410             }
22411             if(y < s.top){
22412                 y = s.top;
22413                 moved = true;
22414             }
22415             if(moved){
22416                 if(this.avoidY){
22417                     var ay = this.avoidY;
22418                     if(y <= ay && (y+h) >= ay){
22419                         y = ay-h-5;   
22420                     }
22421                 }
22422                 xy = [x, y];
22423                 this.storeXY(xy);
22424                 supr.setXY.call(this, xy);
22425                 this.sync();
22426             }
22427         }
22428     },
22429
22430     isVisible : function(){
22431         return this.visible;    
22432     },
22433
22434     // private
22435     showAction : function(){
22436         this.visible = true; // track visibility to prevent getStyle calls
22437         if(this.useDisplay === true){
22438             this.setDisplayed("");
22439         }else if(this.lastXY){
22440             supr.setXY.call(this, this.lastXY);
22441         }else if(this.lastLT){
22442             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22443         }
22444     },
22445
22446     // private
22447     hideAction : function(){
22448         this.visible = false;
22449         if(this.useDisplay === true){
22450             this.setDisplayed(false);
22451         }else{
22452             this.setLeftTop(-10000,-10000);
22453         }
22454     },
22455
22456     // overridden Element method
22457     setVisible : function(v, a, d, c, e){
22458         if(v){
22459             this.showAction();
22460         }
22461         if(a && v){
22462             var cb = function(){
22463                 this.sync(true);
22464                 if(c){
22465                     c();
22466                 }
22467             }.createDelegate(this);
22468             supr.setVisible.call(this, true, true, d, cb, e);
22469         }else{
22470             if(!v){
22471                 this.hideUnders(true);
22472             }
22473             var cb = c;
22474             if(a){
22475                 cb = function(){
22476                     this.hideAction();
22477                     if(c){
22478                         c();
22479                     }
22480                 }.createDelegate(this);
22481             }
22482             supr.setVisible.call(this, v, a, d, cb, e);
22483             if(v){
22484                 this.sync(true);
22485             }else if(!a){
22486                 this.hideAction();
22487             }
22488         }
22489     },
22490
22491     storeXY : function(xy){
22492         delete this.lastLT;
22493         this.lastXY = xy;
22494     },
22495
22496     storeLeftTop : function(left, top){
22497         delete this.lastXY;
22498         this.lastLT = [left, top];
22499     },
22500
22501     // private
22502     beforeFx : function(){
22503         this.beforeAction();
22504         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22505     },
22506
22507     // private
22508     afterFx : function(){
22509         Roo.Layer.superclass.afterFx.apply(this, arguments);
22510         this.sync(this.isVisible());
22511     },
22512
22513     // private
22514     beforeAction : function(){
22515         if(!this.updating && this.shadow){
22516             this.shadow.hide();
22517         }
22518     },
22519
22520     // overridden Element method
22521     setLeft : function(left){
22522         this.storeLeftTop(left, this.getTop(true));
22523         supr.setLeft.apply(this, arguments);
22524         this.sync();
22525     },
22526
22527     setTop : function(top){
22528         this.storeLeftTop(this.getLeft(true), top);
22529         supr.setTop.apply(this, arguments);
22530         this.sync();
22531     },
22532
22533     setLeftTop : function(left, top){
22534         this.storeLeftTop(left, top);
22535         supr.setLeftTop.apply(this, arguments);
22536         this.sync();
22537     },
22538
22539     setXY : function(xy, a, d, c, e){
22540         this.fixDisplay();
22541         this.beforeAction();
22542         this.storeXY(xy);
22543         var cb = this.createCB(c);
22544         supr.setXY.call(this, xy, a, d, cb, e);
22545         if(!a){
22546             cb();
22547         }
22548     },
22549
22550     // private
22551     createCB : function(c){
22552         var el = this;
22553         return function(){
22554             el.constrainXY();
22555             el.sync(true);
22556             if(c){
22557                 c();
22558             }
22559         };
22560     },
22561
22562     // overridden Element method
22563     setX : function(x, a, d, c, e){
22564         this.setXY([x, this.getY()], a, d, c, e);
22565     },
22566
22567     // overridden Element method
22568     setY : function(y, a, d, c, e){
22569         this.setXY([this.getX(), y], a, d, c, e);
22570     },
22571
22572     // overridden Element method
22573     setSize : function(w, h, a, d, c, e){
22574         this.beforeAction();
22575         var cb = this.createCB(c);
22576         supr.setSize.call(this, w, h, a, d, cb, e);
22577         if(!a){
22578             cb();
22579         }
22580     },
22581
22582     // overridden Element method
22583     setWidth : function(w, a, d, c, e){
22584         this.beforeAction();
22585         var cb = this.createCB(c);
22586         supr.setWidth.call(this, w, a, d, cb, e);
22587         if(!a){
22588             cb();
22589         }
22590     },
22591
22592     // overridden Element method
22593     setHeight : function(h, a, d, c, e){
22594         this.beforeAction();
22595         var cb = this.createCB(c);
22596         supr.setHeight.call(this, h, a, d, cb, e);
22597         if(!a){
22598             cb();
22599         }
22600     },
22601
22602     // overridden Element method
22603     setBounds : function(x, y, w, h, a, d, c, e){
22604         this.beforeAction();
22605         var cb = this.createCB(c);
22606         if(!a){
22607             this.storeXY([x, y]);
22608             supr.setXY.call(this, [x, y]);
22609             supr.setSize.call(this, w, h, a, d, cb, e);
22610             cb();
22611         }else{
22612             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22613         }
22614         return this;
22615     },
22616     
22617     /**
22618      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22619      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22620      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22621      * @param {Number} zindex The new z-index to set
22622      * @return {this} The Layer
22623      */
22624     setZIndex : function(zindex){
22625         this.zindex = zindex;
22626         this.setStyle("z-index", zindex + 2);
22627         if(this.shadow){
22628             this.shadow.setZIndex(zindex + 1);
22629         }
22630         if(this.shim){
22631             this.shim.setStyle("z-index", zindex);
22632         }
22633     }
22634 });
22635 })();/*
22636  * Based on:
22637  * Ext JS Library 1.1.1
22638  * Copyright(c) 2006-2007, Ext JS, LLC.
22639  *
22640  * Originally Released Under LGPL - original licence link has changed is not relivant.
22641  *
22642  * Fork - LGPL
22643  * <script type="text/javascript">
22644  */
22645
22646
22647 /**
22648  * @class Roo.Shadow
22649  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22650  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22651  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22652  * @constructor
22653  * Create a new Shadow
22654  * @param {Object} config The config object
22655  */
22656 Roo.Shadow = function(config){
22657     Roo.apply(this, config);
22658     if(typeof this.mode != "string"){
22659         this.mode = this.defaultMode;
22660     }
22661     var o = this.offset, a = {h: 0};
22662     var rad = Math.floor(this.offset/2);
22663     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22664         case "drop":
22665             a.w = 0;
22666             a.l = a.t = o;
22667             a.t -= 1;
22668             if(Roo.isIE){
22669                 a.l -= this.offset + rad;
22670                 a.t -= this.offset + rad;
22671                 a.w -= rad;
22672                 a.h -= rad;
22673                 a.t += 1;
22674             }
22675         break;
22676         case "sides":
22677             a.w = (o*2);
22678             a.l = -o;
22679             a.t = o-1;
22680             if(Roo.isIE){
22681                 a.l -= (this.offset - rad);
22682                 a.t -= this.offset + rad;
22683                 a.l += 1;
22684                 a.w -= (this.offset - rad)*2;
22685                 a.w -= rad + 1;
22686                 a.h -= 1;
22687             }
22688         break;
22689         case "frame":
22690             a.w = a.h = (o*2);
22691             a.l = a.t = -o;
22692             a.t += 1;
22693             a.h -= 2;
22694             if(Roo.isIE){
22695                 a.l -= (this.offset - rad);
22696                 a.t -= (this.offset - rad);
22697                 a.l += 1;
22698                 a.w -= (this.offset + rad + 1);
22699                 a.h -= (this.offset + rad);
22700                 a.h += 1;
22701             }
22702         break;
22703     };
22704
22705     this.adjusts = a;
22706 };
22707
22708 Roo.Shadow.prototype = {
22709     /**
22710      * @cfg {String} mode
22711      * The shadow display mode.  Supports the following options:<br />
22712      * sides: Shadow displays on both sides and bottom only<br />
22713      * frame: Shadow displays equally on all four sides<br />
22714      * drop: Traditional bottom-right drop shadow (default)
22715      */
22716     /**
22717      * @cfg {String} offset
22718      * The number of pixels to offset the shadow from the element (defaults to 4)
22719      */
22720     offset: 4,
22721
22722     // private
22723     defaultMode: "drop",
22724
22725     /**
22726      * Displays the shadow under the target element
22727      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22728      */
22729     show : function(target){
22730         target = Roo.get(target);
22731         if(!this.el){
22732             this.el = Roo.Shadow.Pool.pull();
22733             if(this.el.dom.nextSibling != target.dom){
22734                 this.el.insertBefore(target);
22735             }
22736         }
22737         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22738         if(Roo.isIE){
22739             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22740         }
22741         this.realign(
22742             target.getLeft(true),
22743             target.getTop(true),
22744             target.getWidth(),
22745             target.getHeight()
22746         );
22747         this.el.dom.style.display = "block";
22748     },
22749
22750     /**
22751      * Returns true if the shadow is visible, else false
22752      */
22753     isVisible : function(){
22754         return this.el ? true : false;  
22755     },
22756
22757     /**
22758      * Direct alignment when values are already available. Show must be called at least once before
22759      * calling this method to ensure it is initialized.
22760      * @param {Number} left The target element left position
22761      * @param {Number} top The target element top position
22762      * @param {Number} width The target element width
22763      * @param {Number} height The target element height
22764      */
22765     realign : function(l, t, w, h){
22766         if(!this.el){
22767             return;
22768         }
22769         var a = this.adjusts, d = this.el.dom, s = d.style;
22770         var iea = 0;
22771         s.left = (l+a.l)+"px";
22772         s.top = (t+a.t)+"px";
22773         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22774  
22775         if(s.width != sws || s.height != shs){
22776             s.width = sws;
22777             s.height = shs;
22778             if(!Roo.isIE){
22779                 var cn = d.childNodes;
22780                 var sww = Math.max(0, (sw-12))+"px";
22781                 cn[0].childNodes[1].style.width = sww;
22782                 cn[1].childNodes[1].style.width = sww;
22783                 cn[2].childNodes[1].style.width = sww;
22784                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22785             }
22786         }
22787     },
22788
22789     /**
22790      * Hides this shadow
22791      */
22792     hide : function(){
22793         if(this.el){
22794             this.el.dom.style.display = "none";
22795             Roo.Shadow.Pool.push(this.el);
22796             delete this.el;
22797         }
22798     },
22799
22800     /**
22801      * Adjust the z-index of this shadow
22802      * @param {Number} zindex The new z-index
22803      */
22804     setZIndex : function(z){
22805         this.zIndex = z;
22806         if(this.el){
22807             this.el.setStyle("z-index", z);
22808         }
22809     }
22810 };
22811
22812 // Private utility class that manages the internal Shadow cache
22813 Roo.Shadow.Pool = function(){
22814     var p = [];
22815     var markup = Roo.isIE ?
22816                  '<div class="x-ie-shadow"></div>' :
22817                  '<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>';
22818     return {
22819         pull : function(){
22820             var sh = p.shift();
22821             if(!sh){
22822                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22823                 sh.autoBoxAdjust = false;
22824             }
22825             return sh;
22826         },
22827
22828         push : function(sh){
22829             p.push(sh);
22830         }
22831     };
22832 }();/*
22833  * Based on:
22834  * Ext JS Library 1.1.1
22835  * Copyright(c) 2006-2007, Ext JS, LLC.
22836  *
22837  * Originally Released Under LGPL - original licence link has changed is not relivant.
22838  *
22839  * Fork - LGPL
22840  * <script type="text/javascript">
22841  */
22842
22843 /**
22844  * @class Roo.BoxComponent
22845  * @extends Roo.Component
22846  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22847  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22848  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22849  * layout containers.
22850  * @constructor
22851  * @param {Roo.Element/String/Object} config The configuration options.
22852  */
22853 Roo.BoxComponent = function(config){
22854     Roo.Component.call(this, config);
22855     this.addEvents({
22856         /**
22857          * @event resize
22858          * Fires after the component is resized.
22859              * @param {Roo.Component} this
22860              * @param {Number} adjWidth The box-adjusted width that was set
22861              * @param {Number} adjHeight The box-adjusted height that was set
22862              * @param {Number} rawWidth The width that was originally specified
22863              * @param {Number} rawHeight The height that was originally specified
22864              */
22865         resize : true,
22866         /**
22867          * @event move
22868          * Fires after the component is moved.
22869              * @param {Roo.Component} this
22870              * @param {Number} x The new x position
22871              * @param {Number} y The new y position
22872              */
22873         move : true
22874     });
22875 };
22876
22877 Roo.extend(Roo.BoxComponent, Roo.Component, {
22878     // private, set in afterRender to signify that the component has been rendered
22879     boxReady : false,
22880     // private, used to defer height settings to subclasses
22881     deferHeight: false,
22882     /** @cfg {Number} width
22883      * width (optional) size of component
22884      */
22885      /** @cfg {Number} height
22886      * height (optional) size of component
22887      */
22888      
22889     /**
22890      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22891      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22892      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22893      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22894      * @return {Roo.BoxComponent} this
22895      */
22896     setSize : function(w, h){
22897         // support for standard size objects
22898         if(typeof w == 'object'){
22899             h = w.height;
22900             w = w.width;
22901         }
22902         // not rendered
22903         if(!this.boxReady){
22904             this.width = w;
22905             this.height = h;
22906             return this;
22907         }
22908
22909         // prevent recalcs when not needed
22910         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22911             return this;
22912         }
22913         this.lastSize = {width: w, height: h};
22914
22915         var adj = this.adjustSize(w, h);
22916         var aw = adj.width, ah = adj.height;
22917         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22918             var rz = this.getResizeEl();
22919             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22920                 rz.setSize(aw, ah);
22921             }else if(!this.deferHeight && ah !== undefined){
22922                 rz.setHeight(ah);
22923             }else if(aw !== undefined){
22924                 rz.setWidth(aw);
22925             }
22926             this.onResize(aw, ah, w, h);
22927             this.fireEvent('resize', this, aw, ah, w, h);
22928         }
22929         return this;
22930     },
22931
22932     /**
22933      * Gets the current size of the component's underlying element.
22934      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22935      */
22936     getSize : function(){
22937         return this.el.getSize();
22938     },
22939
22940     /**
22941      * Gets the current XY position of the component's underlying element.
22942      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22943      * @return {Array} The XY position of the element (e.g., [100, 200])
22944      */
22945     getPosition : function(local){
22946         if(local === true){
22947             return [this.el.getLeft(true), this.el.getTop(true)];
22948         }
22949         return this.xy || this.el.getXY();
22950     },
22951
22952     /**
22953      * Gets the current box measurements of the component's underlying element.
22954      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22955      * @returns {Object} box An object in the format {x, y, width, height}
22956      */
22957     getBox : function(local){
22958         var s = this.el.getSize();
22959         if(local){
22960             s.x = this.el.getLeft(true);
22961             s.y = this.el.getTop(true);
22962         }else{
22963             var xy = this.xy || this.el.getXY();
22964             s.x = xy[0];
22965             s.y = xy[1];
22966         }
22967         return s;
22968     },
22969
22970     /**
22971      * Sets the current box measurements of the component's underlying element.
22972      * @param {Object} box An object in the format {x, y, width, height}
22973      * @returns {Roo.BoxComponent} this
22974      */
22975     updateBox : function(box){
22976         this.setSize(box.width, box.height);
22977         this.setPagePosition(box.x, box.y);
22978         return this;
22979     },
22980
22981     // protected
22982     getResizeEl : function(){
22983         return this.resizeEl || this.el;
22984     },
22985
22986     // protected
22987     getPositionEl : function(){
22988         return this.positionEl || this.el;
22989     },
22990
22991     /**
22992      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22993      * This method fires the move event.
22994      * @param {Number} left The new left
22995      * @param {Number} top The new top
22996      * @returns {Roo.BoxComponent} this
22997      */
22998     setPosition : function(x, y){
22999         this.x = x;
23000         this.y = y;
23001         if(!this.boxReady){
23002             return this;
23003         }
23004         var adj = this.adjustPosition(x, y);
23005         var ax = adj.x, ay = adj.y;
23006
23007         var el = this.getPositionEl();
23008         if(ax !== undefined || ay !== undefined){
23009             if(ax !== undefined && ay !== undefined){
23010                 el.setLeftTop(ax, ay);
23011             }else if(ax !== undefined){
23012                 el.setLeft(ax);
23013             }else if(ay !== undefined){
23014                 el.setTop(ay);
23015             }
23016             this.onPosition(ax, ay);
23017             this.fireEvent('move', this, ax, ay);
23018         }
23019         return this;
23020     },
23021
23022     /**
23023      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23024      * This method fires the move event.
23025      * @param {Number} x The new x position
23026      * @param {Number} y The new y position
23027      * @returns {Roo.BoxComponent} this
23028      */
23029     setPagePosition : function(x, y){
23030         this.pageX = x;
23031         this.pageY = y;
23032         if(!this.boxReady){
23033             return;
23034         }
23035         if(x === undefined || y === undefined){ // cannot translate undefined points
23036             return;
23037         }
23038         var p = this.el.translatePoints(x, y);
23039         this.setPosition(p.left, p.top);
23040         return this;
23041     },
23042
23043     // private
23044     onRender : function(ct, position){
23045         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23046         if(this.resizeEl){
23047             this.resizeEl = Roo.get(this.resizeEl);
23048         }
23049         if(this.positionEl){
23050             this.positionEl = Roo.get(this.positionEl);
23051         }
23052     },
23053
23054     // private
23055     afterRender : function(){
23056         Roo.BoxComponent.superclass.afterRender.call(this);
23057         this.boxReady = true;
23058         this.setSize(this.width, this.height);
23059         if(this.x || this.y){
23060             this.setPosition(this.x, this.y);
23061         }
23062         if(this.pageX || this.pageY){
23063             this.setPagePosition(this.pageX, this.pageY);
23064         }
23065     },
23066
23067     /**
23068      * Force the component's size to recalculate based on the underlying element's current height and width.
23069      * @returns {Roo.BoxComponent} this
23070      */
23071     syncSize : function(){
23072         delete this.lastSize;
23073         this.setSize(this.el.getWidth(), this.el.getHeight());
23074         return this;
23075     },
23076
23077     /**
23078      * Called after the component is resized, this method is empty by default but can be implemented by any
23079      * subclass that needs to perform custom logic after a resize occurs.
23080      * @param {Number} adjWidth The box-adjusted width that was set
23081      * @param {Number} adjHeight The box-adjusted height that was set
23082      * @param {Number} rawWidth The width that was originally specified
23083      * @param {Number} rawHeight The height that was originally specified
23084      */
23085     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23086
23087     },
23088
23089     /**
23090      * Called after the component is moved, this method is empty by default but can be implemented by any
23091      * subclass that needs to perform custom logic after a move occurs.
23092      * @param {Number} x The new x position
23093      * @param {Number} y The new y position
23094      */
23095     onPosition : function(x, y){
23096
23097     },
23098
23099     // private
23100     adjustSize : function(w, h){
23101         if(this.autoWidth){
23102             w = 'auto';
23103         }
23104         if(this.autoHeight){
23105             h = 'auto';
23106         }
23107         return {width : w, height: h};
23108     },
23109
23110     // private
23111     adjustPosition : function(x, y){
23112         return {x : x, y: y};
23113     }
23114 });/*
23115  * Based on:
23116  * Ext JS Library 1.1.1
23117  * Copyright(c) 2006-2007, Ext JS, LLC.
23118  *
23119  * Originally Released Under LGPL - original licence link has changed is not relivant.
23120  *
23121  * Fork - LGPL
23122  * <script type="text/javascript">
23123  */
23124
23125
23126 /**
23127  * @class Roo.SplitBar
23128  * @extends Roo.util.Observable
23129  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23130  * <br><br>
23131  * Usage:
23132  * <pre><code>
23133 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23134                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23135 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23136 split.minSize = 100;
23137 split.maxSize = 600;
23138 split.animate = true;
23139 split.on('moved', splitterMoved);
23140 </code></pre>
23141  * @constructor
23142  * Create a new SplitBar
23143  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23144  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23145  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23146  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23147                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23148                         position of the SplitBar).
23149  */
23150 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23151     
23152     /** @private */
23153     this.el = Roo.get(dragElement, true);
23154     this.el.dom.unselectable = "on";
23155     /** @private */
23156     this.resizingEl = Roo.get(resizingElement, true);
23157
23158     /**
23159      * @private
23160      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23161      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23162      * @type Number
23163      */
23164     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23165     
23166     /**
23167      * The minimum size of the resizing element. (Defaults to 0)
23168      * @type Number
23169      */
23170     this.minSize = 0;
23171     
23172     /**
23173      * The maximum size of the resizing element. (Defaults to 2000)
23174      * @type Number
23175      */
23176     this.maxSize = 2000;
23177     
23178     /**
23179      * Whether to animate the transition to the new size
23180      * @type Boolean
23181      */
23182     this.animate = false;
23183     
23184     /**
23185      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23186      * @type Boolean
23187      */
23188     this.useShim = false;
23189     
23190     /** @private */
23191     this.shim = null;
23192     
23193     if(!existingProxy){
23194         /** @private */
23195         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23196     }else{
23197         this.proxy = Roo.get(existingProxy).dom;
23198     }
23199     /** @private */
23200     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23201     
23202     /** @private */
23203     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23204     
23205     /** @private */
23206     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23207     
23208     /** @private */
23209     this.dragSpecs = {};
23210     
23211     /**
23212      * @private The adapter to use to positon and resize elements
23213      */
23214     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23215     this.adapter.init(this);
23216     
23217     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23218         /** @private */
23219         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23220         this.el.addClass("x-splitbar-h");
23221     }else{
23222         /** @private */
23223         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23224         this.el.addClass("x-splitbar-v");
23225     }
23226     
23227     this.addEvents({
23228         /**
23229          * @event resize
23230          * Fires when the splitter is moved (alias for {@link #event-moved})
23231          * @param {Roo.SplitBar} this
23232          * @param {Number} newSize the new width or height
23233          */
23234         "resize" : true,
23235         /**
23236          * @event moved
23237          * Fires when the splitter is moved
23238          * @param {Roo.SplitBar} this
23239          * @param {Number} newSize the new width or height
23240          */
23241         "moved" : true,
23242         /**
23243          * @event beforeresize
23244          * Fires before the splitter is dragged
23245          * @param {Roo.SplitBar} this
23246          */
23247         "beforeresize" : true,
23248
23249         "beforeapply" : true
23250     });
23251
23252     Roo.util.Observable.call(this);
23253 };
23254
23255 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23256     onStartProxyDrag : function(x, y){
23257         this.fireEvent("beforeresize", this);
23258         if(!this.overlay){
23259             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23260             o.unselectable();
23261             o.enableDisplayMode("block");
23262             // all splitbars share the same overlay
23263             Roo.SplitBar.prototype.overlay = o;
23264         }
23265         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23266         this.overlay.show();
23267         Roo.get(this.proxy).setDisplayed("block");
23268         var size = this.adapter.getElementSize(this);
23269         this.activeMinSize = this.getMinimumSize();;
23270         this.activeMaxSize = this.getMaximumSize();;
23271         var c1 = size - this.activeMinSize;
23272         var c2 = Math.max(this.activeMaxSize - size, 0);
23273         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23274             this.dd.resetConstraints();
23275             this.dd.setXConstraint(
23276                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23277                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23278             );
23279             this.dd.setYConstraint(0, 0);
23280         }else{
23281             this.dd.resetConstraints();
23282             this.dd.setXConstraint(0, 0);
23283             this.dd.setYConstraint(
23284                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23285                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23286             );
23287          }
23288         this.dragSpecs.startSize = size;
23289         this.dragSpecs.startPoint = [x, y];
23290         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23291     },
23292     
23293     /** 
23294      * @private Called after the drag operation by the DDProxy
23295      */
23296     onEndProxyDrag : function(e){
23297         Roo.get(this.proxy).setDisplayed(false);
23298         var endPoint = Roo.lib.Event.getXY(e);
23299         if(this.overlay){
23300             this.overlay.hide();
23301         }
23302         var newSize;
23303         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23304             newSize = this.dragSpecs.startSize + 
23305                 (this.placement == Roo.SplitBar.LEFT ?
23306                     endPoint[0] - this.dragSpecs.startPoint[0] :
23307                     this.dragSpecs.startPoint[0] - endPoint[0]
23308                 );
23309         }else{
23310             newSize = this.dragSpecs.startSize + 
23311                 (this.placement == Roo.SplitBar.TOP ?
23312                     endPoint[1] - this.dragSpecs.startPoint[1] :
23313                     this.dragSpecs.startPoint[1] - endPoint[1]
23314                 );
23315         }
23316         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23317         if(newSize != this.dragSpecs.startSize){
23318             if(this.fireEvent('beforeapply', this, newSize) !== false){
23319                 this.adapter.setElementSize(this, newSize);
23320                 this.fireEvent("moved", this, newSize);
23321                 this.fireEvent("resize", this, newSize);
23322             }
23323         }
23324     },
23325     
23326     /**
23327      * Get the adapter this SplitBar uses
23328      * @return The adapter object
23329      */
23330     getAdapter : function(){
23331         return this.adapter;
23332     },
23333     
23334     /**
23335      * Set the adapter this SplitBar uses
23336      * @param {Object} adapter A SplitBar adapter object
23337      */
23338     setAdapter : function(adapter){
23339         this.adapter = adapter;
23340         this.adapter.init(this);
23341     },
23342     
23343     /**
23344      * Gets the minimum size for the resizing element
23345      * @return {Number} The minimum size
23346      */
23347     getMinimumSize : function(){
23348         return this.minSize;
23349     },
23350     
23351     /**
23352      * Sets the minimum size for the resizing element
23353      * @param {Number} minSize The minimum size
23354      */
23355     setMinimumSize : function(minSize){
23356         this.minSize = minSize;
23357     },
23358     
23359     /**
23360      * Gets the maximum size for the resizing element
23361      * @return {Number} The maximum size
23362      */
23363     getMaximumSize : function(){
23364         return this.maxSize;
23365     },
23366     
23367     /**
23368      * Sets the maximum size for the resizing element
23369      * @param {Number} maxSize The maximum size
23370      */
23371     setMaximumSize : function(maxSize){
23372         this.maxSize = maxSize;
23373     },
23374     
23375     /**
23376      * Sets the initialize size for the resizing element
23377      * @param {Number} size The initial size
23378      */
23379     setCurrentSize : function(size){
23380         var oldAnimate = this.animate;
23381         this.animate = false;
23382         this.adapter.setElementSize(this, size);
23383         this.animate = oldAnimate;
23384     },
23385     
23386     /**
23387      * Destroy this splitbar. 
23388      * @param {Boolean} removeEl True to remove the element
23389      */
23390     destroy : function(removeEl){
23391         if(this.shim){
23392             this.shim.remove();
23393         }
23394         this.dd.unreg();
23395         this.proxy.parentNode.removeChild(this.proxy);
23396         if(removeEl){
23397             this.el.remove();
23398         }
23399     }
23400 });
23401
23402 /**
23403  * @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.
23404  */
23405 Roo.SplitBar.createProxy = function(dir){
23406     var proxy = new Roo.Element(document.createElement("div"));
23407     proxy.unselectable();
23408     var cls = 'x-splitbar-proxy';
23409     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23410     document.body.appendChild(proxy.dom);
23411     return proxy.dom;
23412 };
23413
23414 /** 
23415  * @class Roo.SplitBar.BasicLayoutAdapter
23416  * Default Adapter. It assumes the splitter and resizing element are not positioned
23417  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23418  */
23419 Roo.SplitBar.BasicLayoutAdapter = function(){
23420 };
23421
23422 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23423     // do nothing for now
23424     init : function(s){
23425     
23426     },
23427     /**
23428      * Called before drag operations to get the current size of the resizing element. 
23429      * @param {Roo.SplitBar} s The SplitBar using this adapter
23430      */
23431      getElementSize : function(s){
23432         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23433             return s.resizingEl.getWidth();
23434         }else{
23435             return s.resizingEl.getHeight();
23436         }
23437     },
23438     
23439     /**
23440      * Called after drag operations to set the size of the resizing element.
23441      * @param {Roo.SplitBar} s The SplitBar using this adapter
23442      * @param {Number} newSize The new size to set
23443      * @param {Function} onComplete A function to be invoked when resizing is complete
23444      */
23445     setElementSize : function(s, newSize, onComplete){
23446         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23447             if(!s.animate){
23448                 s.resizingEl.setWidth(newSize);
23449                 if(onComplete){
23450                     onComplete(s, newSize);
23451                 }
23452             }else{
23453                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23454             }
23455         }else{
23456             
23457             if(!s.animate){
23458                 s.resizingEl.setHeight(newSize);
23459                 if(onComplete){
23460                     onComplete(s, newSize);
23461                 }
23462             }else{
23463                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23464             }
23465         }
23466     }
23467 };
23468
23469 /** 
23470  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23471  * @extends Roo.SplitBar.BasicLayoutAdapter
23472  * Adapter that  moves the splitter element to align with the resized sizing element. 
23473  * Used with an absolute positioned SplitBar.
23474  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23475  * document.body, make sure you assign an id to the body element.
23476  */
23477 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23478     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23479     this.container = Roo.get(container);
23480 };
23481
23482 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23483     init : function(s){
23484         this.basic.init(s);
23485     },
23486     
23487     getElementSize : function(s){
23488         return this.basic.getElementSize(s);
23489     },
23490     
23491     setElementSize : function(s, newSize, onComplete){
23492         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23493     },
23494     
23495     moveSplitter : function(s){
23496         var yes = Roo.SplitBar;
23497         switch(s.placement){
23498             case yes.LEFT:
23499                 s.el.setX(s.resizingEl.getRight());
23500                 break;
23501             case yes.RIGHT:
23502                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23503                 break;
23504             case yes.TOP:
23505                 s.el.setY(s.resizingEl.getBottom());
23506                 break;
23507             case yes.BOTTOM:
23508                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23509                 break;
23510         }
23511     }
23512 };
23513
23514 /**
23515  * Orientation constant - Create a vertical SplitBar
23516  * @static
23517  * @type Number
23518  */
23519 Roo.SplitBar.VERTICAL = 1;
23520
23521 /**
23522  * Orientation constant - Create a horizontal SplitBar
23523  * @static
23524  * @type Number
23525  */
23526 Roo.SplitBar.HORIZONTAL = 2;
23527
23528 /**
23529  * Placement constant - The resizing element is to the left of the splitter element
23530  * @static
23531  * @type Number
23532  */
23533 Roo.SplitBar.LEFT = 1;
23534
23535 /**
23536  * Placement constant - The resizing element is to the right of the splitter element
23537  * @static
23538  * @type Number
23539  */
23540 Roo.SplitBar.RIGHT = 2;
23541
23542 /**
23543  * Placement constant - The resizing element is positioned above the splitter element
23544  * @static
23545  * @type Number
23546  */
23547 Roo.SplitBar.TOP = 3;
23548
23549 /**
23550  * Placement constant - The resizing element is positioned under splitter element
23551  * @static
23552  * @type Number
23553  */
23554 Roo.SplitBar.BOTTOM = 4;
23555 /*
23556  * Based on:
23557  * Ext JS Library 1.1.1
23558  * Copyright(c) 2006-2007, Ext JS, LLC.
23559  *
23560  * Originally Released Under LGPL - original licence link has changed is not relivant.
23561  *
23562  * Fork - LGPL
23563  * <script type="text/javascript">
23564  */
23565
23566 /**
23567  * @class Roo.View
23568  * @extends Roo.util.Observable
23569  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23570  * This class also supports single and multi selection modes. <br>
23571  * Create a data model bound view:
23572  <pre><code>
23573  var store = new Roo.data.Store(...);
23574
23575  var view = new Roo.View({
23576     el : "my-element",
23577     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23578  
23579     singleSelect: true,
23580     selectedClass: "ydataview-selected",
23581     store: store
23582  });
23583
23584  // listen for node click?
23585  view.on("click", function(vw, index, node, e){
23586  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23587  });
23588
23589  // load XML data
23590  dataModel.load("foobar.xml");
23591  </code></pre>
23592  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23593  * <br><br>
23594  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23595  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23596  * 
23597  * Note: old style constructor is still suported (container, template, config)
23598  * 
23599  * @constructor
23600  * Create a new View
23601  * @param {Object} config The config object
23602  * 
23603  */
23604 Roo.View = function(config, depreciated_tpl, depreciated_config){
23605     
23606     if (typeof(depreciated_tpl) == 'undefined') {
23607         // new way.. - universal constructor.
23608         Roo.apply(this, config);
23609         this.el  = Roo.get(this.el);
23610     } else {
23611         // old format..
23612         this.el  = Roo.get(config);
23613         this.tpl = depreciated_tpl;
23614         Roo.apply(this, depreciated_config);
23615     }
23616      
23617     
23618     if(typeof(this.tpl) == "string"){
23619         this.tpl = new Roo.Template(this.tpl);
23620     } else {
23621         // support xtype ctors..
23622         this.tpl = new Roo.factory(this.tpl, Roo);
23623     }
23624     
23625     
23626     this.tpl.compile();
23627    
23628
23629      
23630     /** @private */
23631     this.addEvents({
23632         /**
23633          * @event beforeclick
23634          * Fires before a click is processed. Returns false to cancel the default action.
23635          * @param {Roo.View} this
23636          * @param {Number} index The index of the target node
23637          * @param {HTMLElement} node The target node
23638          * @param {Roo.EventObject} e The raw event object
23639          */
23640             "beforeclick" : true,
23641         /**
23642          * @event click
23643          * Fires when a template node is clicked.
23644          * @param {Roo.View} this
23645          * @param {Number} index The index of the target node
23646          * @param {HTMLElement} node The target node
23647          * @param {Roo.EventObject} e The raw event object
23648          */
23649             "click" : true,
23650         /**
23651          * @event dblclick
23652          * Fires when a template node is double clicked.
23653          * @param {Roo.View} this
23654          * @param {Number} index The index of the target node
23655          * @param {HTMLElement} node The target node
23656          * @param {Roo.EventObject} e The raw event object
23657          */
23658             "dblclick" : true,
23659         /**
23660          * @event contextmenu
23661          * Fires when a template node is right clicked.
23662          * @param {Roo.View} this
23663          * @param {Number} index The index of the target node
23664          * @param {HTMLElement} node The target node
23665          * @param {Roo.EventObject} e The raw event object
23666          */
23667             "contextmenu" : true,
23668         /**
23669          * @event selectionchange
23670          * Fires when the selected nodes change.
23671          * @param {Roo.View} this
23672          * @param {Array} selections Array of the selected nodes
23673          */
23674             "selectionchange" : true,
23675     
23676         /**
23677          * @event beforeselect
23678          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23679          * @param {Roo.View} this
23680          * @param {HTMLElement} node The node to be selected
23681          * @param {Array} selections Array of currently selected nodes
23682          */
23683             "beforeselect" : true,
23684         /**
23685          * @event preparedata
23686          * Fires on every row to render, to allow you to change the data.
23687          * @param {Roo.View} this
23688          * @param {Object} data to be rendered (change this)
23689          */
23690           "preparedata" : true
23691         });
23692
23693     this.el.on({
23694         "click": this.onClick,
23695         "dblclick": this.onDblClick,
23696         "contextmenu": this.onContextMenu,
23697         scope:this
23698     });
23699
23700     this.selections = [];
23701     this.nodes = [];
23702     this.cmp = new Roo.CompositeElementLite([]);
23703     if(this.store){
23704         this.store = Roo.factory(this.store, Roo.data);
23705         this.setStore(this.store, true);
23706     }
23707     Roo.View.superclass.constructor.call(this);
23708 };
23709
23710 Roo.extend(Roo.View, Roo.util.Observable, {
23711     
23712      /**
23713      * @cfg {Roo.data.Store} store Data store to load data from.
23714      */
23715     store : false,
23716     
23717     /**
23718      * @cfg {String|Roo.Element} el The container element.
23719      */
23720     el : '',
23721     
23722     /**
23723      * @cfg {String|Roo.Template} tpl The template used by this View 
23724      */
23725     tpl : false,
23726     
23727     /**
23728      * @cfg {String} selectedClass The css class to add to selected nodes
23729      */
23730     selectedClass : "x-view-selected",
23731      /**
23732      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23733      */
23734     emptyText : "",
23735     /**
23736      * @cfg {Boolean} multiSelect Allow multiple selection
23737      */
23738     multiSelect : false,
23739     /**
23740      * @cfg {Boolean} singleSelect Allow single selection
23741      */
23742     singleSelect:  false,
23743     
23744     /**
23745      * @cfg {Boolean} toggleSelect - selecting 
23746      */
23747     toggleSelect : false,
23748     
23749     /**
23750      * Returns the element this view is bound to.
23751      * @return {Roo.Element}
23752      */
23753     getEl : function(){
23754         return this.el;
23755     },
23756
23757     /**
23758      * Refreshes the view.
23759      */
23760     refresh : function(){
23761         var t = this.tpl;
23762         this.clearSelections();
23763         this.el.update("");
23764         var html = [];
23765         var records = this.store.getRange();
23766         if(records.length < 1){
23767             this.el.update(this.emptyText);
23768             return;
23769         }
23770         for(var i = 0, len = records.length; i < len; i++){
23771             var data = this.prepareData(records[i].data, i, records[i]);
23772             this.fireEvent("preparedata", this, data, i, records[i]);
23773             html[html.length] = t.apply(data);
23774         }
23775         this.el.update(html.join(""));
23776         this.nodes = this.el.dom.childNodes;
23777         this.updateIndexes(0);
23778     },
23779
23780     /**
23781      * Function to override to reformat the data that is sent to
23782      * the template for each node.
23783      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23784      * a JSON object for an UpdateManager bound view).
23785      */
23786     prepareData : function(data){
23787         return data;
23788     },
23789
23790     onUpdate : function(ds, record){
23791         this.clearSelections();
23792         var index = this.store.indexOf(record);
23793         var n = this.nodes[index];
23794         this.tpl.insertBefore(n, this.prepareData(record.data));
23795         n.parentNode.removeChild(n);
23796         this.updateIndexes(index, index);
23797     },
23798
23799     onAdd : function(ds, records, index){
23800         this.clearSelections();
23801         if(this.nodes.length == 0){
23802             this.refresh();
23803             return;
23804         }
23805         var n = this.nodes[index];
23806         for(var i = 0, len = records.length; i < len; i++){
23807             var d = this.prepareData(records[i].data);
23808             if(n){
23809                 this.tpl.insertBefore(n, d);
23810             }else{
23811                 this.tpl.append(this.el, d);
23812             }
23813         }
23814         this.updateIndexes(index);
23815     },
23816
23817     onRemove : function(ds, record, index){
23818         this.clearSelections();
23819         this.el.dom.removeChild(this.nodes[index]);
23820         this.updateIndexes(index);
23821     },
23822
23823     /**
23824      * Refresh an individual node.
23825      * @param {Number} index
23826      */
23827     refreshNode : function(index){
23828         this.onUpdate(this.store, this.store.getAt(index));
23829     },
23830
23831     updateIndexes : function(startIndex, endIndex){
23832         var ns = this.nodes;
23833         startIndex = startIndex || 0;
23834         endIndex = endIndex || ns.length - 1;
23835         for(var i = startIndex; i <= endIndex; i++){
23836             ns[i].nodeIndex = i;
23837         }
23838     },
23839
23840     /**
23841      * Changes the data store this view uses and refresh the view.
23842      * @param {Store} store
23843      */
23844     setStore : function(store, initial){
23845         if(!initial && this.store){
23846             this.store.un("datachanged", this.refresh);
23847             this.store.un("add", this.onAdd);
23848             this.store.un("remove", this.onRemove);
23849             this.store.un("update", this.onUpdate);
23850             this.store.un("clear", this.refresh);
23851         }
23852         if(store){
23853           
23854             store.on("datachanged", this.refresh, this);
23855             store.on("add", this.onAdd, this);
23856             store.on("remove", this.onRemove, this);
23857             store.on("update", this.onUpdate, this);
23858             store.on("clear", this.refresh, this);
23859         }
23860         
23861         if(store){
23862             this.refresh();
23863         }
23864     },
23865
23866     /**
23867      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23868      * @param {HTMLElement} node
23869      * @return {HTMLElement} The template node
23870      */
23871     findItemFromChild : function(node){
23872         var el = this.el.dom;
23873         if(!node || node.parentNode == el){
23874                     return node;
23875             }
23876             var p = node.parentNode;
23877             while(p && p != el){
23878             if(p.parentNode == el){
23879                 return p;
23880             }
23881             p = p.parentNode;
23882         }
23883             return null;
23884     },
23885
23886     /** @ignore */
23887     onClick : function(e){
23888         var item = this.findItemFromChild(e.getTarget());
23889         if(item){
23890             var index = this.indexOf(item);
23891             if(this.onItemClick(item, index, e) !== false){
23892                 this.fireEvent("click", this, index, item, e);
23893             }
23894         }else{
23895             this.clearSelections();
23896         }
23897     },
23898
23899     /** @ignore */
23900     onContextMenu : function(e){
23901         var item = this.findItemFromChild(e.getTarget());
23902         if(item){
23903             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23904         }
23905     },
23906
23907     /** @ignore */
23908     onDblClick : function(e){
23909         var item = this.findItemFromChild(e.getTarget());
23910         if(item){
23911             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23912         }
23913     },
23914
23915     onItemClick : function(item, index, e)
23916     {
23917         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23918             return false;
23919         }
23920         if (this.toggleSelect) {
23921             var m = this.isSelected(item) ? 'unselect' : 'select';
23922             Roo.log(m);
23923             var _t = this;
23924             _t[m](item, true, false);
23925             return true;
23926         }
23927         if(this.multiSelect || this.singleSelect){
23928             if(this.multiSelect && e.shiftKey && this.lastSelection){
23929                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23930             }else{
23931                 this.select(item, this.multiSelect && e.ctrlKey);
23932                 this.lastSelection = item;
23933             }
23934             e.preventDefault();
23935         }
23936         return true;
23937     },
23938
23939     /**
23940      * Get the number of selected nodes.
23941      * @return {Number}
23942      */
23943     getSelectionCount : function(){
23944         return this.selections.length;
23945     },
23946
23947     /**
23948      * Get the currently selected nodes.
23949      * @return {Array} An array of HTMLElements
23950      */
23951     getSelectedNodes : function(){
23952         return this.selections;
23953     },
23954
23955     /**
23956      * Get the indexes of the selected nodes.
23957      * @return {Array}
23958      */
23959     getSelectedIndexes : function(){
23960         var indexes = [], s = this.selections;
23961         for(var i = 0, len = s.length; i < len; i++){
23962             indexes.push(s[i].nodeIndex);
23963         }
23964         return indexes;
23965     },
23966
23967     /**
23968      * Clear all selections
23969      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23970      */
23971     clearSelections : function(suppressEvent){
23972         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23973             this.cmp.elements = this.selections;
23974             this.cmp.removeClass(this.selectedClass);
23975             this.selections = [];
23976             if(!suppressEvent){
23977                 this.fireEvent("selectionchange", this, this.selections);
23978             }
23979         }
23980     },
23981
23982     /**
23983      * Returns true if the passed node is selected
23984      * @param {HTMLElement/Number} node The node or node index
23985      * @return {Boolean}
23986      */
23987     isSelected : function(node){
23988         var s = this.selections;
23989         if(s.length < 1){
23990             return false;
23991         }
23992         node = this.getNode(node);
23993         return s.indexOf(node) !== -1;
23994     },
23995
23996     /**
23997      * Selects nodes.
23998      * @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
23999      * @param {Boolean} keepExisting (optional) true to keep existing selections
24000      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24001      */
24002     select : function(nodeInfo, keepExisting, suppressEvent){
24003         if(nodeInfo instanceof Array){
24004             if(!keepExisting){
24005                 this.clearSelections(true);
24006             }
24007             for(var i = 0, len = nodeInfo.length; i < len; i++){
24008                 this.select(nodeInfo[i], true, true);
24009             }
24010             return;
24011         } 
24012         var node = this.getNode(nodeInfo);
24013         if(!node || this.isSelected(node)){
24014             return; // already selected.
24015         }
24016         if(!keepExisting){
24017             this.clearSelections(true);
24018         }
24019         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24020             Roo.fly(node).addClass(this.selectedClass);
24021             this.selections.push(node);
24022             if(!suppressEvent){
24023                 this.fireEvent("selectionchange", this, this.selections);
24024             }
24025         }
24026         
24027         
24028     },
24029       /**
24030      * Unselects nodes.
24031      * @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
24032      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24033      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24034      */
24035     unselect : function(nodeInfo, keepExisting, suppressEvent)
24036     {
24037         if(nodeInfo instanceof Array){
24038             Roo.each(this.selections, function(s) {
24039                 this.unselect(s, nodeInfo);
24040             }, this);
24041             return;
24042         }
24043         var node = this.getNode(nodeInfo);
24044         if(!node || !this.isSelected(node)){
24045             Roo.log("not selected");
24046             return; // not selected.
24047         }
24048         // fireevent???
24049         var ns = [];
24050         Roo.each(this.selections, function(s) {
24051             if (s == node ) {
24052                 Roo.fly(node).removeClass(this.selectedClass);
24053
24054                 return;
24055             }
24056             ns.push(s);
24057         },this);
24058         
24059         this.selections= ns;
24060         this.fireEvent("selectionchange", this, this.selections);
24061     },
24062
24063     /**
24064      * Gets a template node.
24065      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24066      * @return {HTMLElement} The node or null if it wasn't found
24067      */
24068     getNode : function(nodeInfo){
24069         if(typeof nodeInfo == "string"){
24070             return document.getElementById(nodeInfo);
24071         }else if(typeof nodeInfo == "number"){
24072             return this.nodes[nodeInfo];
24073         }
24074         return nodeInfo;
24075     },
24076
24077     /**
24078      * Gets a range template nodes.
24079      * @param {Number} startIndex
24080      * @param {Number} endIndex
24081      * @return {Array} An array of nodes
24082      */
24083     getNodes : function(start, end){
24084         var ns = this.nodes;
24085         start = start || 0;
24086         end = typeof end == "undefined" ? ns.length - 1 : end;
24087         var nodes = [];
24088         if(start <= end){
24089             for(var i = start; i <= end; i++){
24090                 nodes.push(ns[i]);
24091             }
24092         } else{
24093             for(var i = start; i >= end; i--){
24094                 nodes.push(ns[i]);
24095             }
24096         }
24097         return nodes;
24098     },
24099
24100     /**
24101      * Finds the index of the passed 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 {Number} The index of the node or -1
24104      */
24105     indexOf : function(node){
24106         node = this.getNode(node);
24107         if(typeof node.nodeIndex == "number"){
24108             return node.nodeIndex;
24109         }
24110         var ns = this.nodes;
24111         for(var i = 0, len = ns.length; i < len; i++){
24112             if(ns[i] == node){
24113                 return i;
24114             }
24115         }
24116         return -1;
24117     }
24118 });
24119 /*
24120  * Based on:
24121  * Ext JS Library 1.1.1
24122  * Copyright(c) 2006-2007, Ext JS, LLC.
24123  *
24124  * Originally Released Under LGPL - original licence link has changed is not relivant.
24125  *
24126  * Fork - LGPL
24127  * <script type="text/javascript">
24128  */
24129
24130 /**
24131  * @class Roo.JsonView
24132  * @extends Roo.View
24133  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24134 <pre><code>
24135 var view = new Roo.JsonView({
24136     container: "my-element",
24137     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24138     multiSelect: true, 
24139     jsonRoot: "data" 
24140 });
24141
24142 // listen for node click?
24143 view.on("click", function(vw, index, node, e){
24144     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24145 });
24146
24147 // direct load of JSON data
24148 view.load("foobar.php");
24149
24150 // Example from my blog list
24151 var tpl = new Roo.Template(
24152     '&lt;div class="entry"&gt;' +
24153     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24154     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24155     "&lt;/div&gt;&lt;hr /&gt;"
24156 );
24157
24158 var moreView = new Roo.JsonView({
24159     container :  "entry-list", 
24160     template : tpl,
24161     jsonRoot: "posts"
24162 });
24163 moreView.on("beforerender", this.sortEntries, this);
24164 moreView.load({
24165     url: "/blog/get-posts.php",
24166     params: "allposts=true",
24167     text: "Loading Blog Entries..."
24168 });
24169 </code></pre>
24170
24171 * Note: old code is supported with arguments : (container, template, config)
24172
24173
24174  * @constructor
24175  * Create a new JsonView
24176  * 
24177  * @param {Object} config The config object
24178  * 
24179  */
24180 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24181     
24182     
24183     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24184
24185     var um = this.el.getUpdateManager();
24186     um.setRenderer(this);
24187     um.on("update", this.onLoad, this);
24188     um.on("failure", this.onLoadException, this);
24189
24190     /**
24191      * @event beforerender
24192      * Fires before rendering of the downloaded JSON data.
24193      * @param {Roo.JsonView} this
24194      * @param {Object} data The JSON data loaded
24195      */
24196     /**
24197      * @event load
24198      * Fires when data is loaded.
24199      * @param {Roo.JsonView} this
24200      * @param {Object} data The JSON data loaded
24201      * @param {Object} response The raw Connect response object
24202      */
24203     /**
24204      * @event loadexception
24205      * Fires when loading fails.
24206      * @param {Roo.JsonView} this
24207      * @param {Object} response The raw Connect response object
24208      */
24209     this.addEvents({
24210         'beforerender' : true,
24211         'load' : true,
24212         'loadexception' : true
24213     });
24214 };
24215 Roo.extend(Roo.JsonView, Roo.View, {
24216     /**
24217      * @type {String} The root property in the loaded JSON object that contains the data
24218      */
24219     jsonRoot : "",
24220
24221     /**
24222      * Refreshes the view.
24223      */
24224     refresh : function(){
24225         this.clearSelections();
24226         this.el.update("");
24227         var html = [];
24228         var o = this.jsonData;
24229         if(o && o.length > 0){
24230             for(var i = 0, len = o.length; i < len; i++){
24231                 var data = this.prepareData(o[i], i, o);
24232                 html[html.length] = this.tpl.apply(data);
24233             }
24234         }else{
24235             html.push(this.emptyText);
24236         }
24237         this.el.update(html.join(""));
24238         this.nodes = this.el.dom.childNodes;
24239         this.updateIndexes(0);
24240     },
24241
24242     /**
24243      * 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.
24244      * @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:
24245      <pre><code>
24246      view.load({
24247          url: "your-url.php",
24248          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24249          callback: yourFunction,
24250          scope: yourObject, //(optional scope)
24251          discardUrl: false,
24252          nocache: false,
24253          text: "Loading...",
24254          timeout: 30,
24255          scripts: false
24256      });
24257      </code></pre>
24258      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24259      * 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.
24260      * @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}
24261      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24262      * @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.
24263      */
24264     load : function(){
24265         var um = this.el.getUpdateManager();
24266         um.update.apply(um, arguments);
24267     },
24268
24269     render : function(el, response){
24270         this.clearSelections();
24271         this.el.update("");
24272         var o;
24273         try{
24274             o = Roo.util.JSON.decode(response.responseText);
24275             if(this.jsonRoot){
24276                 
24277                 o = o[this.jsonRoot];
24278             }
24279         } catch(e){
24280         }
24281         /**
24282          * The current JSON data or null
24283          */
24284         this.jsonData = o;
24285         this.beforeRender();
24286         this.refresh();
24287     },
24288
24289 /**
24290  * Get the number of records in the current JSON dataset
24291  * @return {Number}
24292  */
24293     getCount : function(){
24294         return this.jsonData ? this.jsonData.length : 0;
24295     },
24296
24297 /**
24298  * Returns the JSON object for the specified node(s)
24299  * @param {HTMLElement/Array} node The node or an array of nodes
24300  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24301  * you get the JSON object for the node
24302  */
24303     getNodeData : function(node){
24304         if(node instanceof Array){
24305             var data = [];
24306             for(var i = 0, len = node.length; i < len; i++){
24307                 data.push(this.getNodeData(node[i]));
24308             }
24309             return data;
24310         }
24311         return this.jsonData[this.indexOf(node)] || null;
24312     },
24313
24314     beforeRender : function(){
24315         this.snapshot = this.jsonData;
24316         if(this.sortInfo){
24317             this.sort.apply(this, this.sortInfo);
24318         }
24319         this.fireEvent("beforerender", this, this.jsonData);
24320     },
24321
24322     onLoad : function(el, o){
24323         this.fireEvent("load", this, this.jsonData, o);
24324     },
24325
24326     onLoadException : function(el, o){
24327         this.fireEvent("loadexception", this, o);
24328     },
24329
24330 /**
24331  * Filter the data by a specific property.
24332  * @param {String} property A property on your JSON objects
24333  * @param {String/RegExp} value Either string that the property values
24334  * should start with, or a RegExp to test against the property
24335  */
24336     filter : function(property, value){
24337         if(this.jsonData){
24338             var data = [];
24339             var ss = this.snapshot;
24340             if(typeof value == "string"){
24341                 var vlen = value.length;
24342                 if(vlen == 0){
24343                     this.clearFilter();
24344                     return;
24345                 }
24346                 value = value.toLowerCase();
24347                 for(var i = 0, len = ss.length; i < len; i++){
24348                     var o = ss[i];
24349                     if(o[property].substr(0, vlen).toLowerCase() == value){
24350                         data.push(o);
24351                     }
24352                 }
24353             } else if(value.exec){ // regex?
24354                 for(var i = 0, len = ss.length; i < len; i++){
24355                     var o = ss[i];
24356                     if(value.test(o[property])){
24357                         data.push(o);
24358                     }
24359                 }
24360             } else{
24361                 return;
24362             }
24363             this.jsonData = data;
24364             this.refresh();
24365         }
24366     },
24367
24368 /**
24369  * Filter by a function. The passed function will be called with each
24370  * object in the current dataset. If the function returns true the value is kept,
24371  * otherwise it is filtered.
24372  * @param {Function} fn
24373  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24374  */
24375     filterBy : function(fn, scope){
24376         if(this.jsonData){
24377             var data = [];
24378             var ss = this.snapshot;
24379             for(var i = 0, len = ss.length; i < len; i++){
24380                 var o = ss[i];
24381                 if(fn.call(scope || this, o)){
24382                     data.push(o);
24383                 }
24384             }
24385             this.jsonData = data;
24386             this.refresh();
24387         }
24388     },
24389
24390 /**
24391  * Clears the current filter.
24392  */
24393     clearFilter : function(){
24394         if(this.snapshot && this.jsonData != this.snapshot){
24395             this.jsonData = this.snapshot;
24396             this.refresh();
24397         }
24398     },
24399
24400
24401 /**
24402  * Sorts the data for this view and refreshes it.
24403  * @param {String} property A property on your JSON objects to sort on
24404  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24405  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24406  */
24407     sort : function(property, dir, sortType){
24408         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24409         if(this.jsonData){
24410             var p = property;
24411             var dsc = dir && dir.toLowerCase() == "desc";
24412             var f = function(o1, o2){
24413                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24414                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24415                 ;
24416                 if(v1 < v2){
24417                     return dsc ? +1 : -1;
24418                 } else if(v1 > v2){
24419                     return dsc ? -1 : +1;
24420                 } else{
24421                     return 0;
24422                 }
24423             };
24424             this.jsonData.sort(f);
24425             this.refresh();
24426             if(this.jsonData != this.snapshot){
24427                 this.snapshot.sort(f);
24428             }
24429         }
24430     }
24431 });/*
24432  * Based on:
24433  * Ext JS Library 1.1.1
24434  * Copyright(c) 2006-2007, Ext JS, LLC.
24435  *
24436  * Originally Released Under LGPL - original licence link has changed is not relivant.
24437  *
24438  * Fork - LGPL
24439  * <script type="text/javascript">
24440  */
24441  
24442
24443 /**
24444  * @class Roo.ColorPalette
24445  * @extends Roo.Component
24446  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24447  * Here's an example of typical usage:
24448  * <pre><code>
24449 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24450 cp.render('my-div');
24451
24452 cp.on('select', function(palette, selColor){
24453     // do something with selColor
24454 });
24455 </code></pre>
24456  * @constructor
24457  * Create a new ColorPalette
24458  * @param {Object} config The config object
24459  */
24460 Roo.ColorPalette = function(config){
24461     Roo.ColorPalette.superclass.constructor.call(this, config);
24462     this.addEvents({
24463         /**
24464              * @event select
24465              * Fires when a color is selected
24466              * @param {ColorPalette} this
24467              * @param {String} color The 6-digit color hex code (without the # symbol)
24468              */
24469         select: true
24470     });
24471
24472     if(this.handler){
24473         this.on("select", this.handler, this.scope, true);
24474     }
24475 };
24476 Roo.extend(Roo.ColorPalette, Roo.Component, {
24477     /**
24478      * @cfg {String} itemCls
24479      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24480      */
24481     itemCls : "x-color-palette",
24482     /**
24483      * @cfg {String} value
24484      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24485      * the hex codes are case-sensitive.
24486      */
24487     value : null,
24488     clickEvent:'click',
24489     // private
24490     ctype: "Roo.ColorPalette",
24491
24492     /**
24493      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24494      */
24495     allowReselect : false,
24496
24497     /**
24498      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24499      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24500      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24501      * of colors with the width setting until the box is symmetrical.</p>
24502      * <p>You can override individual colors if needed:</p>
24503      * <pre><code>
24504 var cp = new Roo.ColorPalette();
24505 cp.colors[0] = "FF0000";  // change the first box to red
24506 </code></pre>
24507
24508 Or you can provide a custom array of your own for complete control:
24509 <pre><code>
24510 var cp = new Roo.ColorPalette();
24511 cp.colors = ["000000", "993300", "333300"];
24512 </code></pre>
24513      * @type Array
24514      */
24515     colors : [
24516         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24517         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24518         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24519         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24520         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24521     ],
24522
24523     // private
24524     onRender : function(container, position){
24525         var t = new Roo.MasterTemplate(
24526             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24527         );
24528         var c = this.colors;
24529         for(var i = 0, len = c.length; i < len; i++){
24530             t.add([c[i]]);
24531         }
24532         var el = document.createElement("div");
24533         el.className = this.itemCls;
24534         t.overwrite(el);
24535         container.dom.insertBefore(el, position);
24536         this.el = Roo.get(el);
24537         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24538         if(this.clickEvent != 'click'){
24539             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24540         }
24541     },
24542
24543     // private
24544     afterRender : function(){
24545         Roo.ColorPalette.superclass.afterRender.call(this);
24546         if(this.value){
24547             var s = this.value;
24548             this.value = null;
24549             this.select(s);
24550         }
24551     },
24552
24553     // private
24554     handleClick : function(e, t){
24555         e.preventDefault();
24556         if(!this.disabled){
24557             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24558             this.select(c.toUpperCase());
24559         }
24560     },
24561
24562     /**
24563      * Selects the specified color in the palette (fires the select event)
24564      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24565      */
24566     select : function(color){
24567         color = color.replace("#", "");
24568         if(color != this.value || this.allowReselect){
24569             var el = this.el;
24570             if(this.value){
24571                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24572             }
24573             el.child("a.color-"+color).addClass("x-color-palette-sel");
24574             this.value = color;
24575             this.fireEvent("select", this, color);
24576         }
24577     }
24578 });/*
24579  * Based on:
24580  * Ext JS Library 1.1.1
24581  * Copyright(c) 2006-2007, Ext JS, LLC.
24582  *
24583  * Originally Released Under LGPL - original licence link has changed is not relivant.
24584  *
24585  * Fork - LGPL
24586  * <script type="text/javascript">
24587  */
24588  
24589 /**
24590  * @class Roo.DatePicker
24591  * @extends Roo.Component
24592  * Simple date picker class.
24593  * @constructor
24594  * Create a new DatePicker
24595  * @param {Object} config The config object
24596  */
24597 Roo.DatePicker = function(config){
24598     Roo.DatePicker.superclass.constructor.call(this, config);
24599
24600     this.value = config && config.value ?
24601                  config.value.clearTime() : new Date().clearTime();
24602
24603     this.addEvents({
24604         /**
24605              * @event select
24606              * Fires when a date is selected
24607              * @param {DatePicker} this
24608              * @param {Date} date The selected date
24609              */
24610         'select': true,
24611         /**
24612              * @event monthchange
24613              * Fires when the displayed month changes 
24614              * @param {DatePicker} this
24615              * @param {Date} date The selected month
24616              */
24617         'monthchange': true
24618     });
24619
24620     if(this.handler){
24621         this.on("select", this.handler,  this.scope || this);
24622     }
24623     // build the disabledDatesRE
24624     if(!this.disabledDatesRE && this.disabledDates){
24625         var dd = this.disabledDates;
24626         var re = "(?:";
24627         for(var i = 0; i < dd.length; i++){
24628             re += dd[i];
24629             if(i != dd.length-1) re += "|";
24630         }
24631         this.disabledDatesRE = new RegExp(re + ")");
24632     }
24633 };
24634
24635 Roo.extend(Roo.DatePicker, Roo.Component, {
24636     /**
24637      * @cfg {String} todayText
24638      * The text to display on the button that selects the current date (defaults to "Today")
24639      */
24640     todayText : "Today",
24641     /**
24642      * @cfg {String} okText
24643      * The text to display on the ok button
24644      */
24645     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24646     /**
24647      * @cfg {String} cancelText
24648      * The text to display on the cancel button
24649      */
24650     cancelText : "Cancel",
24651     /**
24652      * @cfg {String} todayTip
24653      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24654      */
24655     todayTip : "{0} (Spacebar)",
24656     /**
24657      * @cfg {Date} minDate
24658      * Minimum allowable date (JavaScript date object, defaults to null)
24659      */
24660     minDate : null,
24661     /**
24662      * @cfg {Date} maxDate
24663      * Maximum allowable date (JavaScript date object, defaults to null)
24664      */
24665     maxDate : null,
24666     /**
24667      * @cfg {String} minText
24668      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24669      */
24670     minText : "This date is before the minimum date",
24671     /**
24672      * @cfg {String} maxText
24673      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24674      */
24675     maxText : "This date is after the maximum date",
24676     /**
24677      * @cfg {String} format
24678      * The default date format string which can be overriden for localization support.  The format must be
24679      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24680      */
24681     format : "m/d/y",
24682     /**
24683      * @cfg {Array} disabledDays
24684      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24685      */
24686     disabledDays : null,
24687     /**
24688      * @cfg {String} disabledDaysText
24689      * The tooltip to display when the date falls on a disabled day (defaults to "")
24690      */
24691     disabledDaysText : "",
24692     /**
24693      * @cfg {RegExp} disabledDatesRE
24694      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24695      */
24696     disabledDatesRE : null,
24697     /**
24698      * @cfg {String} disabledDatesText
24699      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24700      */
24701     disabledDatesText : "",
24702     /**
24703      * @cfg {Boolean} constrainToViewport
24704      * True to constrain the date picker to the viewport (defaults to true)
24705      */
24706     constrainToViewport : true,
24707     /**
24708      * @cfg {Array} monthNames
24709      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24710      */
24711     monthNames : Date.monthNames,
24712     /**
24713      * @cfg {Array} dayNames
24714      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24715      */
24716     dayNames : Date.dayNames,
24717     /**
24718      * @cfg {String} nextText
24719      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24720      */
24721     nextText: 'Next Month (Control+Right)',
24722     /**
24723      * @cfg {String} prevText
24724      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24725      */
24726     prevText: 'Previous Month (Control+Left)',
24727     /**
24728      * @cfg {String} monthYearText
24729      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24730      */
24731     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24732     /**
24733      * @cfg {Number} startDay
24734      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24735      */
24736     startDay : 0,
24737     /**
24738      * @cfg {Bool} showClear
24739      * Show a clear button (usefull for date form elements that can be blank.)
24740      */
24741     
24742     showClear: false,
24743     
24744     /**
24745      * Sets the value of the date field
24746      * @param {Date} value The date to set
24747      */
24748     setValue : function(value){
24749         var old = this.value;
24750         this.value = value.clearTime(true);
24751         if(this.el){
24752             this.update(this.value);
24753         }
24754     },
24755
24756     /**
24757      * Gets the current selected value of the date field
24758      * @return {Date} The selected date
24759      */
24760     getValue : function(){
24761         return this.value;
24762     },
24763
24764     // private
24765     focus : function(){
24766         if(this.el){
24767             this.update(this.activeDate);
24768         }
24769     },
24770
24771     // private
24772     onRender : function(container, position){
24773         var m = [
24774              '<table cellspacing="0">',
24775                 '<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>',
24776                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24777         var dn = this.dayNames;
24778         for(var i = 0; i < 7; i++){
24779             var d = this.startDay+i;
24780             if(d > 6){
24781                 d = d-7;
24782             }
24783             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24784         }
24785         m[m.length] = "</tr></thead><tbody><tr>";
24786         for(var i = 0; i < 42; i++) {
24787             if(i % 7 == 0 && i != 0){
24788                 m[m.length] = "</tr><tr>";
24789             }
24790             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24791         }
24792         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24793             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24794
24795         var el = document.createElement("div");
24796         el.className = "x-date-picker";
24797         el.innerHTML = m.join("");
24798
24799         container.dom.insertBefore(el, position);
24800
24801         this.el = Roo.get(el);
24802         this.eventEl = Roo.get(el.firstChild);
24803
24804         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24805             handler: this.showPrevMonth,
24806             scope: this,
24807             preventDefault:true,
24808             stopDefault:true
24809         });
24810
24811         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24812             handler: this.showNextMonth,
24813             scope: this,
24814             preventDefault:true,
24815             stopDefault:true
24816         });
24817
24818         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24819
24820         this.monthPicker = this.el.down('div.x-date-mp');
24821         this.monthPicker.enableDisplayMode('block');
24822         
24823         var kn = new Roo.KeyNav(this.eventEl, {
24824             "left" : function(e){
24825                 e.ctrlKey ?
24826                     this.showPrevMonth() :
24827                     this.update(this.activeDate.add("d", -1));
24828             },
24829
24830             "right" : function(e){
24831                 e.ctrlKey ?
24832                     this.showNextMonth() :
24833                     this.update(this.activeDate.add("d", 1));
24834             },
24835
24836             "up" : function(e){
24837                 e.ctrlKey ?
24838                     this.showNextYear() :
24839                     this.update(this.activeDate.add("d", -7));
24840             },
24841
24842             "down" : function(e){
24843                 e.ctrlKey ?
24844                     this.showPrevYear() :
24845                     this.update(this.activeDate.add("d", 7));
24846             },
24847
24848             "pageUp" : function(e){
24849                 this.showNextMonth();
24850             },
24851
24852             "pageDown" : function(e){
24853                 this.showPrevMonth();
24854             },
24855
24856             "enter" : function(e){
24857                 e.stopPropagation();
24858                 return true;
24859             },
24860
24861             scope : this
24862         });
24863
24864         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24865
24866         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24867
24868         this.el.unselectable();
24869         
24870         this.cells = this.el.select("table.x-date-inner tbody td");
24871         this.textNodes = this.el.query("table.x-date-inner tbody span");
24872
24873         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24874             text: "&#160;",
24875             tooltip: this.monthYearText
24876         });
24877
24878         this.mbtn.on('click', this.showMonthPicker, this);
24879         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24880
24881
24882         var today = (new Date()).dateFormat(this.format);
24883         
24884         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24885         if (this.showClear) {
24886             baseTb.add( new Roo.Toolbar.Fill());
24887         }
24888         baseTb.add({
24889             text: String.format(this.todayText, today),
24890             tooltip: String.format(this.todayTip, today),
24891             handler: this.selectToday,
24892             scope: this
24893         });
24894         
24895         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24896             
24897         //});
24898         if (this.showClear) {
24899             
24900             baseTb.add( new Roo.Toolbar.Fill());
24901             baseTb.add({
24902                 text: '&#160;',
24903                 cls: 'x-btn-icon x-btn-clear',
24904                 handler: function() {
24905                     //this.value = '';
24906                     this.fireEvent("select", this, '');
24907                 },
24908                 scope: this
24909             });
24910         }
24911         
24912         
24913         if(Roo.isIE){
24914             this.el.repaint();
24915         }
24916         this.update(this.value);
24917     },
24918
24919     createMonthPicker : function(){
24920         if(!this.monthPicker.dom.firstChild){
24921             var buf = ['<table border="0" cellspacing="0">'];
24922             for(var i = 0; i < 6; i++){
24923                 buf.push(
24924                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24925                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24926                     i == 0 ?
24927                     '<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>' :
24928                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24929                 );
24930             }
24931             buf.push(
24932                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24933                     this.okText,
24934                     '</button><button type="button" class="x-date-mp-cancel">',
24935                     this.cancelText,
24936                     '</button></td></tr>',
24937                 '</table>'
24938             );
24939             this.monthPicker.update(buf.join(''));
24940             this.monthPicker.on('click', this.onMonthClick, this);
24941             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24942
24943             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24944             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24945
24946             this.mpMonths.each(function(m, a, i){
24947                 i += 1;
24948                 if((i%2) == 0){
24949                     m.dom.xmonth = 5 + Math.round(i * .5);
24950                 }else{
24951                     m.dom.xmonth = Math.round((i-1) * .5);
24952                 }
24953             });
24954         }
24955     },
24956
24957     showMonthPicker : function(){
24958         this.createMonthPicker();
24959         var size = this.el.getSize();
24960         this.monthPicker.setSize(size);
24961         this.monthPicker.child('table').setSize(size);
24962
24963         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24964         this.updateMPMonth(this.mpSelMonth);
24965         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24966         this.updateMPYear(this.mpSelYear);
24967
24968         this.monthPicker.slideIn('t', {duration:.2});
24969     },
24970
24971     updateMPYear : function(y){
24972         this.mpyear = y;
24973         var ys = this.mpYears.elements;
24974         for(var i = 1; i <= 10; i++){
24975             var td = ys[i-1], y2;
24976             if((i%2) == 0){
24977                 y2 = y + Math.round(i * .5);
24978                 td.firstChild.innerHTML = y2;
24979                 td.xyear = y2;
24980             }else{
24981                 y2 = y - (5-Math.round(i * .5));
24982                 td.firstChild.innerHTML = y2;
24983                 td.xyear = y2;
24984             }
24985             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24986         }
24987     },
24988
24989     updateMPMonth : function(sm){
24990         this.mpMonths.each(function(m, a, i){
24991             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24992         });
24993     },
24994
24995     selectMPMonth: function(m){
24996         
24997     },
24998
24999     onMonthClick : function(e, t){
25000         e.stopEvent();
25001         var el = new Roo.Element(t), pn;
25002         if(el.is('button.x-date-mp-cancel')){
25003             this.hideMonthPicker();
25004         }
25005         else if(el.is('button.x-date-mp-ok')){
25006             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25007             this.hideMonthPicker();
25008         }
25009         else if(pn = el.up('td.x-date-mp-month', 2)){
25010             this.mpMonths.removeClass('x-date-mp-sel');
25011             pn.addClass('x-date-mp-sel');
25012             this.mpSelMonth = pn.dom.xmonth;
25013         }
25014         else if(pn = el.up('td.x-date-mp-year', 2)){
25015             this.mpYears.removeClass('x-date-mp-sel');
25016             pn.addClass('x-date-mp-sel');
25017             this.mpSelYear = pn.dom.xyear;
25018         }
25019         else if(el.is('a.x-date-mp-prev')){
25020             this.updateMPYear(this.mpyear-10);
25021         }
25022         else if(el.is('a.x-date-mp-next')){
25023             this.updateMPYear(this.mpyear+10);
25024         }
25025     },
25026
25027     onMonthDblClick : function(e, t){
25028         e.stopEvent();
25029         var el = new Roo.Element(t), pn;
25030         if(pn = el.up('td.x-date-mp-month', 2)){
25031             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25032             this.hideMonthPicker();
25033         }
25034         else if(pn = el.up('td.x-date-mp-year', 2)){
25035             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25036             this.hideMonthPicker();
25037         }
25038     },
25039
25040     hideMonthPicker : function(disableAnim){
25041         if(this.monthPicker){
25042             if(disableAnim === true){
25043                 this.monthPicker.hide();
25044             }else{
25045                 this.monthPicker.slideOut('t', {duration:.2});
25046             }
25047         }
25048     },
25049
25050     // private
25051     showPrevMonth : function(e){
25052         this.update(this.activeDate.add("mo", -1));
25053     },
25054
25055     // private
25056     showNextMonth : function(e){
25057         this.update(this.activeDate.add("mo", 1));
25058     },
25059
25060     // private
25061     showPrevYear : function(){
25062         this.update(this.activeDate.add("y", -1));
25063     },
25064
25065     // private
25066     showNextYear : function(){
25067         this.update(this.activeDate.add("y", 1));
25068     },
25069
25070     // private
25071     handleMouseWheel : function(e){
25072         var delta = e.getWheelDelta();
25073         if(delta > 0){
25074             this.showPrevMonth();
25075             e.stopEvent();
25076         } else if(delta < 0){
25077             this.showNextMonth();
25078             e.stopEvent();
25079         }
25080     },
25081
25082     // private
25083     handleDateClick : function(e, t){
25084         e.stopEvent();
25085         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25086             this.setValue(new Date(t.dateValue));
25087             this.fireEvent("select", this, this.value);
25088         }
25089     },
25090
25091     // private
25092     selectToday : function(){
25093         this.setValue(new Date().clearTime());
25094         this.fireEvent("select", this, this.value);
25095     },
25096
25097     // private
25098     update : function(date)
25099     {
25100         var vd = this.activeDate;
25101         this.activeDate = date;
25102         if(vd && this.el){
25103             var t = date.getTime();
25104             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25105                 this.cells.removeClass("x-date-selected");
25106                 this.cells.each(function(c){
25107                    if(c.dom.firstChild.dateValue == t){
25108                        c.addClass("x-date-selected");
25109                        setTimeout(function(){
25110                             try{c.dom.firstChild.focus();}catch(e){}
25111                        }, 50);
25112                        return false;
25113                    }
25114                 });
25115                 return;
25116             }
25117         }
25118         
25119         var days = date.getDaysInMonth();
25120         var firstOfMonth = date.getFirstDateOfMonth();
25121         var startingPos = firstOfMonth.getDay()-this.startDay;
25122
25123         if(startingPos <= this.startDay){
25124             startingPos += 7;
25125         }
25126
25127         var pm = date.add("mo", -1);
25128         var prevStart = pm.getDaysInMonth()-startingPos;
25129
25130         var cells = this.cells.elements;
25131         var textEls = this.textNodes;
25132         days += startingPos;
25133
25134         // convert everything to numbers so it's fast
25135         var day = 86400000;
25136         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25137         var today = new Date().clearTime().getTime();
25138         var sel = date.clearTime().getTime();
25139         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25140         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25141         var ddMatch = this.disabledDatesRE;
25142         var ddText = this.disabledDatesText;
25143         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25144         var ddaysText = this.disabledDaysText;
25145         var format = this.format;
25146
25147         var setCellClass = function(cal, cell){
25148             cell.title = "";
25149             var t = d.getTime();
25150             cell.firstChild.dateValue = t;
25151             if(t == today){
25152                 cell.className += " x-date-today";
25153                 cell.title = cal.todayText;
25154             }
25155             if(t == sel){
25156                 cell.className += " x-date-selected";
25157                 setTimeout(function(){
25158                     try{cell.firstChild.focus();}catch(e){}
25159                 }, 50);
25160             }
25161             // disabling
25162             if(t < min) {
25163                 cell.className = " x-date-disabled";
25164                 cell.title = cal.minText;
25165                 return;
25166             }
25167             if(t > max) {
25168                 cell.className = " x-date-disabled";
25169                 cell.title = cal.maxText;
25170                 return;
25171             }
25172             if(ddays){
25173                 if(ddays.indexOf(d.getDay()) != -1){
25174                     cell.title = ddaysText;
25175                     cell.className = " x-date-disabled";
25176                 }
25177             }
25178             if(ddMatch && format){
25179                 var fvalue = d.dateFormat(format);
25180                 if(ddMatch.test(fvalue)){
25181                     cell.title = ddText.replace("%0", fvalue);
25182                     cell.className = " x-date-disabled";
25183                 }
25184             }
25185         };
25186
25187         var i = 0;
25188         for(; i < startingPos; i++) {
25189             textEls[i].innerHTML = (++prevStart);
25190             d.setDate(d.getDate()+1);
25191             cells[i].className = "x-date-prevday";
25192             setCellClass(this, cells[i]);
25193         }
25194         for(; i < days; i++){
25195             intDay = i - startingPos + 1;
25196             textEls[i].innerHTML = (intDay);
25197             d.setDate(d.getDate()+1);
25198             cells[i].className = "x-date-active";
25199             setCellClass(this, cells[i]);
25200         }
25201         var extraDays = 0;
25202         for(; i < 42; i++) {
25203              textEls[i].innerHTML = (++extraDays);
25204              d.setDate(d.getDate()+1);
25205              cells[i].className = "x-date-nextday";
25206              setCellClass(this, cells[i]);
25207         }
25208
25209         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25210         this.fireEvent('monthchange', this, date);
25211         
25212         if(!this.internalRender){
25213             var main = this.el.dom.firstChild;
25214             var w = main.offsetWidth;
25215             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25216             Roo.fly(main).setWidth(w);
25217             this.internalRender = true;
25218             // opera does not respect the auto grow header center column
25219             // then, after it gets a width opera refuses to recalculate
25220             // without a second pass
25221             if(Roo.isOpera && !this.secondPass){
25222                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25223                 this.secondPass = true;
25224                 this.update.defer(10, this, [date]);
25225             }
25226         }
25227         
25228         
25229     }
25230 });        /*
25231  * Based on:
25232  * Ext JS Library 1.1.1
25233  * Copyright(c) 2006-2007, Ext JS, LLC.
25234  *
25235  * Originally Released Under LGPL - original licence link has changed is not relivant.
25236  *
25237  * Fork - LGPL
25238  * <script type="text/javascript">
25239  */
25240 /**
25241  * @class Roo.TabPanel
25242  * @extends Roo.util.Observable
25243  * A lightweight tab container.
25244  * <br><br>
25245  * Usage:
25246  * <pre><code>
25247 // basic tabs 1, built from existing content
25248 var tabs = new Roo.TabPanel("tabs1");
25249 tabs.addTab("script", "View Script");
25250 tabs.addTab("markup", "View Markup");
25251 tabs.activate("script");
25252
25253 // more advanced tabs, built from javascript
25254 var jtabs = new Roo.TabPanel("jtabs");
25255 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25256
25257 // set up the UpdateManager
25258 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25259 var updater = tab2.getUpdateManager();
25260 updater.setDefaultUrl("ajax1.htm");
25261 tab2.on('activate', updater.refresh, updater, true);
25262
25263 // Use setUrl for Ajax loading
25264 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25265 tab3.setUrl("ajax2.htm", null, true);
25266
25267 // Disabled tab
25268 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25269 tab4.disable();
25270
25271 jtabs.activate("jtabs-1");
25272  * </code></pre>
25273  * @constructor
25274  * Create a new TabPanel.
25275  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25276  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25277  */
25278 Roo.TabPanel = function(container, config){
25279     /**
25280     * The container element for this TabPanel.
25281     * @type Roo.Element
25282     */
25283     this.el = Roo.get(container, true);
25284     if(config){
25285         if(typeof config == "boolean"){
25286             this.tabPosition = config ? "bottom" : "top";
25287         }else{
25288             Roo.apply(this, config);
25289         }
25290     }
25291     if(this.tabPosition == "bottom"){
25292         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25293         this.el.addClass("x-tabs-bottom");
25294     }
25295     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25296     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25297     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25298     if(Roo.isIE){
25299         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25300     }
25301     if(this.tabPosition != "bottom"){
25302         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25303          * @type Roo.Element
25304          */
25305         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25306         this.el.addClass("x-tabs-top");
25307     }
25308     this.items = [];
25309
25310     this.bodyEl.setStyle("position", "relative");
25311
25312     this.active = null;
25313     this.activateDelegate = this.activate.createDelegate(this);
25314
25315     this.addEvents({
25316         /**
25317          * @event tabchange
25318          * Fires when the active tab changes
25319          * @param {Roo.TabPanel} this
25320          * @param {Roo.TabPanelItem} activePanel The new active tab
25321          */
25322         "tabchange": true,
25323         /**
25324          * @event beforetabchange
25325          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25326          * @param {Roo.TabPanel} this
25327          * @param {Object} e Set cancel to true on this object to cancel the tab change
25328          * @param {Roo.TabPanelItem} tab The tab being changed to
25329          */
25330         "beforetabchange" : true
25331     });
25332
25333     Roo.EventManager.onWindowResize(this.onResize, this);
25334     this.cpad = this.el.getPadding("lr");
25335     this.hiddenCount = 0;
25336
25337
25338     // toolbar on the tabbar support...
25339     if (this.toolbar) {
25340         var tcfg = this.toolbar;
25341         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25342         this.toolbar = new Roo.Toolbar(tcfg);
25343         if (Roo.isSafari) {
25344             var tbl = tcfg.container.child('table', true);
25345             tbl.setAttribute('width', '100%');
25346         }
25347         
25348     }
25349    
25350
25351
25352     Roo.TabPanel.superclass.constructor.call(this);
25353 };
25354
25355 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25356     /*
25357      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25358      */
25359     tabPosition : "top",
25360     /*
25361      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25362      */
25363     currentTabWidth : 0,
25364     /*
25365      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25366      */
25367     minTabWidth : 40,
25368     /*
25369      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25370      */
25371     maxTabWidth : 250,
25372     /*
25373      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25374      */
25375     preferredTabWidth : 175,
25376     /*
25377      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25378      */
25379     resizeTabs : false,
25380     /*
25381      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25382      */
25383     monitorResize : true,
25384     /*
25385      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25386      */
25387     toolbar : false,
25388
25389     /**
25390      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25391      * @param {String} id The id of the div to use <b>or create</b>
25392      * @param {String} text The text for the tab
25393      * @param {String} content (optional) Content to put in the TabPanelItem body
25394      * @param {Boolean} closable (optional) True to create a close icon on the tab
25395      * @return {Roo.TabPanelItem} The created TabPanelItem
25396      */
25397     addTab : function(id, text, content, closable){
25398         var item = new Roo.TabPanelItem(this, id, text, closable);
25399         this.addTabItem(item);
25400         if(content){
25401             item.setContent(content);
25402         }
25403         return item;
25404     },
25405
25406     /**
25407      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25408      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25409      * @return {Roo.TabPanelItem}
25410      */
25411     getTab : function(id){
25412         return this.items[id];
25413     },
25414
25415     /**
25416      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25417      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25418      */
25419     hideTab : function(id){
25420         var t = this.items[id];
25421         if(!t.isHidden()){
25422            t.setHidden(true);
25423            this.hiddenCount++;
25424            this.autoSizeTabs();
25425         }
25426     },
25427
25428     /**
25429      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25430      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25431      */
25432     unhideTab : function(id){
25433         var t = this.items[id];
25434         if(t.isHidden()){
25435            t.setHidden(false);
25436            this.hiddenCount--;
25437            this.autoSizeTabs();
25438         }
25439     },
25440
25441     /**
25442      * Adds an existing {@link Roo.TabPanelItem}.
25443      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25444      */
25445     addTabItem : function(item){
25446         this.items[item.id] = item;
25447         this.items.push(item);
25448         if(this.resizeTabs){
25449            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25450            this.autoSizeTabs();
25451         }else{
25452             item.autoSize();
25453         }
25454     },
25455
25456     /**
25457      * Removes a {@link Roo.TabPanelItem}.
25458      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25459      */
25460     removeTab : function(id){
25461         var items = this.items;
25462         var tab = items[id];
25463         if(!tab) { return; }
25464         var index = items.indexOf(tab);
25465         if(this.active == tab && items.length > 1){
25466             var newTab = this.getNextAvailable(index);
25467             if(newTab) {
25468                 newTab.activate();
25469             }
25470         }
25471         this.stripEl.dom.removeChild(tab.pnode.dom);
25472         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25473             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25474         }
25475         items.splice(index, 1);
25476         delete this.items[tab.id];
25477         tab.fireEvent("close", tab);
25478         tab.purgeListeners();
25479         this.autoSizeTabs();
25480     },
25481
25482     getNextAvailable : function(start){
25483         var items = this.items;
25484         var index = start;
25485         // look for a next tab that will slide over to
25486         // replace the one being removed
25487         while(index < items.length){
25488             var item = items[++index];
25489             if(item && !item.isHidden()){
25490                 return item;
25491             }
25492         }
25493         // if one isn't found select the previous tab (on the left)
25494         index = start;
25495         while(index >= 0){
25496             var item = items[--index];
25497             if(item && !item.isHidden()){
25498                 return item;
25499             }
25500         }
25501         return null;
25502     },
25503
25504     /**
25505      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25506      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25507      */
25508     disableTab : function(id){
25509         var tab = this.items[id];
25510         if(tab && this.active != tab){
25511             tab.disable();
25512         }
25513     },
25514
25515     /**
25516      * Enables a {@link Roo.TabPanelItem} that is disabled.
25517      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25518      */
25519     enableTab : function(id){
25520         var tab = this.items[id];
25521         tab.enable();
25522     },
25523
25524     /**
25525      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25526      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25527      * @return {Roo.TabPanelItem} The TabPanelItem.
25528      */
25529     activate : function(id){
25530         var tab = this.items[id];
25531         if(!tab){
25532             return null;
25533         }
25534         if(tab == this.active || tab.disabled){
25535             return tab;
25536         }
25537         var e = {};
25538         this.fireEvent("beforetabchange", this, e, tab);
25539         if(e.cancel !== true && !tab.disabled){
25540             if(this.active){
25541                 this.active.hide();
25542             }
25543             this.active = this.items[id];
25544             this.active.show();
25545             this.fireEvent("tabchange", this, this.active);
25546         }
25547         return tab;
25548     },
25549
25550     /**
25551      * Gets the active {@link Roo.TabPanelItem}.
25552      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25553      */
25554     getActiveTab : function(){
25555         return this.active;
25556     },
25557
25558     /**
25559      * Updates the tab body element to fit the height of the container element
25560      * for overflow scrolling
25561      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25562      */
25563     syncHeight : function(targetHeight){
25564         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25565         var bm = this.bodyEl.getMargins();
25566         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25567         this.bodyEl.setHeight(newHeight);
25568         return newHeight;
25569     },
25570
25571     onResize : function(){
25572         if(this.monitorResize){
25573             this.autoSizeTabs();
25574         }
25575     },
25576
25577     /**
25578      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25579      */
25580     beginUpdate : function(){
25581         this.updating = true;
25582     },
25583
25584     /**
25585      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25586      */
25587     endUpdate : function(){
25588         this.updating = false;
25589         this.autoSizeTabs();
25590     },
25591
25592     /**
25593      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25594      */
25595     autoSizeTabs : function(){
25596         var count = this.items.length;
25597         var vcount = count - this.hiddenCount;
25598         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25599         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25600         var availWidth = Math.floor(w / vcount);
25601         var b = this.stripBody;
25602         if(b.getWidth() > w){
25603             var tabs = this.items;
25604             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25605             if(availWidth < this.minTabWidth){
25606                 /*if(!this.sleft){    // incomplete scrolling code
25607                     this.createScrollButtons();
25608                 }
25609                 this.showScroll();
25610                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25611             }
25612         }else{
25613             if(this.currentTabWidth < this.preferredTabWidth){
25614                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25615             }
25616         }
25617     },
25618
25619     /**
25620      * Returns the number of tabs in this TabPanel.
25621      * @return {Number}
25622      */
25623      getCount : function(){
25624          return this.items.length;
25625      },
25626
25627     /**
25628      * Resizes all the tabs to the passed width
25629      * @param {Number} The new width
25630      */
25631     setTabWidth : function(width){
25632         this.currentTabWidth = width;
25633         for(var i = 0, len = this.items.length; i < len; i++) {
25634                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25635         }
25636     },
25637
25638     /**
25639      * Destroys this TabPanel
25640      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25641      */
25642     destroy : function(removeEl){
25643         Roo.EventManager.removeResizeListener(this.onResize, this);
25644         for(var i = 0, len = this.items.length; i < len; i++){
25645             this.items[i].purgeListeners();
25646         }
25647         if(removeEl === true){
25648             this.el.update("");
25649             this.el.remove();
25650         }
25651     }
25652 });
25653
25654 /**
25655  * @class Roo.TabPanelItem
25656  * @extends Roo.util.Observable
25657  * Represents an individual item (tab plus body) in a TabPanel.
25658  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25659  * @param {String} id The id of this TabPanelItem
25660  * @param {String} text The text for the tab of this TabPanelItem
25661  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25662  */
25663 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25664     /**
25665      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25666      * @type Roo.TabPanel
25667      */
25668     this.tabPanel = tabPanel;
25669     /**
25670      * The id for this TabPanelItem
25671      * @type String
25672      */
25673     this.id = id;
25674     /** @private */
25675     this.disabled = false;
25676     /** @private */
25677     this.text = text;
25678     /** @private */
25679     this.loaded = false;
25680     this.closable = closable;
25681
25682     /**
25683      * The body element for this TabPanelItem.
25684      * @type Roo.Element
25685      */
25686     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25687     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25688     this.bodyEl.setStyle("display", "block");
25689     this.bodyEl.setStyle("zoom", "1");
25690     this.hideAction();
25691
25692     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25693     /** @private */
25694     this.el = Roo.get(els.el, true);
25695     this.inner = Roo.get(els.inner, true);
25696     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25697     this.pnode = Roo.get(els.el.parentNode, true);
25698     this.el.on("mousedown", this.onTabMouseDown, this);
25699     this.el.on("click", this.onTabClick, this);
25700     /** @private */
25701     if(closable){
25702         var c = Roo.get(els.close, true);
25703         c.dom.title = this.closeText;
25704         c.addClassOnOver("close-over");
25705         c.on("click", this.closeClick, this);
25706      }
25707
25708     this.addEvents({
25709          /**
25710          * @event activate
25711          * Fires when this tab becomes the active tab.
25712          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25713          * @param {Roo.TabPanelItem} this
25714          */
25715         "activate": true,
25716         /**
25717          * @event beforeclose
25718          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25719          * @param {Roo.TabPanelItem} this
25720          * @param {Object} e Set cancel to true on this object to cancel the close.
25721          */
25722         "beforeclose": true,
25723         /**
25724          * @event close
25725          * Fires when this tab is closed.
25726          * @param {Roo.TabPanelItem} this
25727          */
25728          "close": true,
25729         /**
25730          * @event deactivate
25731          * Fires when this tab is no longer the active tab.
25732          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25733          * @param {Roo.TabPanelItem} this
25734          */
25735          "deactivate" : true
25736     });
25737     this.hidden = false;
25738
25739     Roo.TabPanelItem.superclass.constructor.call(this);
25740 };
25741
25742 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25743     purgeListeners : function(){
25744        Roo.util.Observable.prototype.purgeListeners.call(this);
25745        this.el.removeAllListeners();
25746     },
25747     /**
25748      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25749      */
25750     show : function(){
25751         this.pnode.addClass("on");
25752         this.showAction();
25753         if(Roo.isOpera){
25754             this.tabPanel.stripWrap.repaint();
25755         }
25756         this.fireEvent("activate", this.tabPanel, this);
25757     },
25758
25759     /**
25760      * Returns true if this tab is the active tab.
25761      * @return {Boolean}
25762      */
25763     isActive : function(){
25764         return this.tabPanel.getActiveTab() == this;
25765     },
25766
25767     /**
25768      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25769      */
25770     hide : function(){
25771         this.pnode.removeClass("on");
25772         this.hideAction();
25773         this.fireEvent("deactivate", this.tabPanel, this);
25774     },
25775
25776     hideAction : function(){
25777         this.bodyEl.hide();
25778         this.bodyEl.setStyle("position", "absolute");
25779         this.bodyEl.setLeft("-20000px");
25780         this.bodyEl.setTop("-20000px");
25781     },
25782
25783     showAction : function(){
25784         this.bodyEl.setStyle("position", "relative");
25785         this.bodyEl.setTop("");
25786         this.bodyEl.setLeft("");
25787         this.bodyEl.show();
25788     },
25789
25790     /**
25791      * Set the tooltip for the tab.
25792      * @param {String} tooltip The tab's tooltip
25793      */
25794     setTooltip : function(text){
25795         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25796             this.textEl.dom.qtip = text;
25797             this.textEl.dom.removeAttribute('title');
25798         }else{
25799             this.textEl.dom.title = text;
25800         }
25801     },
25802
25803     onTabClick : function(e){
25804         e.preventDefault();
25805         this.tabPanel.activate(this.id);
25806     },
25807
25808     onTabMouseDown : function(e){
25809         e.preventDefault();
25810         this.tabPanel.activate(this.id);
25811     },
25812
25813     getWidth : function(){
25814         return this.inner.getWidth();
25815     },
25816
25817     setWidth : function(width){
25818         var iwidth = width - this.pnode.getPadding("lr");
25819         this.inner.setWidth(iwidth);
25820         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25821         this.pnode.setWidth(width);
25822     },
25823
25824     /**
25825      * Show or hide the tab
25826      * @param {Boolean} hidden True to hide or false to show.
25827      */
25828     setHidden : function(hidden){
25829         this.hidden = hidden;
25830         this.pnode.setStyle("display", hidden ? "none" : "");
25831     },
25832
25833     /**
25834      * Returns true if this tab is "hidden"
25835      * @return {Boolean}
25836      */
25837     isHidden : function(){
25838         return this.hidden;
25839     },
25840
25841     /**
25842      * Returns the text for this tab
25843      * @return {String}
25844      */
25845     getText : function(){
25846         return this.text;
25847     },
25848
25849     autoSize : function(){
25850         //this.el.beginMeasure();
25851         this.textEl.setWidth(1);
25852         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25853         //this.el.endMeasure();
25854     },
25855
25856     /**
25857      * Sets the text for the tab (Note: this also sets the tooltip text)
25858      * @param {String} text The tab's text and tooltip
25859      */
25860     setText : function(text){
25861         this.text = text;
25862         this.textEl.update(text);
25863         this.setTooltip(text);
25864         if(!this.tabPanel.resizeTabs){
25865             this.autoSize();
25866         }
25867     },
25868     /**
25869      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25870      */
25871     activate : function(){
25872         this.tabPanel.activate(this.id);
25873     },
25874
25875     /**
25876      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25877      */
25878     disable : function(){
25879         if(this.tabPanel.active != this){
25880             this.disabled = true;
25881             this.pnode.addClass("disabled");
25882         }
25883     },
25884
25885     /**
25886      * Enables this TabPanelItem if it was previously disabled.
25887      */
25888     enable : function(){
25889         this.disabled = false;
25890         this.pnode.removeClass("disabled");
25891     },
25892
25893     /**
25894      * Sets the content for this TabPanelItem.
25895      * @param {String} content The content
25896      * @param {Boolean} loadScripts true to look for and load scripts
25897      */
25898     setContent : function(content, loadScripts){
25899         this.bodyEl.update(content, loadScripts);
25900     },
25901
25902     /**
25903      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25904      * @return {Roo.UpdateManager} The UpdateManager
25905      */
25906     getUpdateManager : function(){
25907         return this.bodyEl.getUpdateManager();
25908     },
25909
25910     /**
25911      * Set a URL to be used to load the content for this TabPanelItem.
25912      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25913      * @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)
25914      * @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)
25915      * @return {Roo.UpdateManager} The UpdateManager
25916      */
25917     setUrl : function(url, params, loadOnce){
25918         if(this.refreshDelegate){
25919             this.un('activate', this.refreshDelegate);
25920         }
25921         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25922         this.on("activate", this.refreshDelegate);
25923         return this.bodyEl.getUpdateManager();
25924     },
25925
25926     /** @private */
25927     _handleRefresh : function(url, params, loadOnce){
25928         if(!loadOnce || !this.loaded){
25929             var updater = this.bodyEl.getUpdateManager();
25930             updater.update(url, params, this._setLoaded.createDelegate(this));
25931         }
25932     },
25933
25934     /**
25935      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25936      *   Will fail silently if the setUrl method has not been called.
25937      *   This does not activate the panel, just updates its content.
25938      */
25939     refresh : function(){
25940         if(this.refreshDelegate){
25941            this.loaded = false;
25942            this.refreshDelegate();
25943         }
25944     },
25945
25946     /** @private */
25947     _setLoaded : function(){
25948         this.loaded = true;
25949     },
25950
25951     /** @private */
25952     closeClick : function(e){
25953         var o = {};
25954         e.stopEvent();
25955         this.fireEvent("beforeclose", this, o);
25956         if(o.cancel !== true){
25957             this.tabPanel.removeTab(this.id);
25958         }
25959     },
25960     /**
25961      * The text displayed in the tooltip for the close icon.
25962      * @type String
25963      */
25964     closeText : "Close this tab"
25965 });
25966
25967 /** @private */
25968 Roo.TabPanel.prototype.createStrip = function(container){
25969     var strip = document.createElement("div");
25970     strip.className = "x-tabs-wrap";
25971     container.appendChild(strip);
25972     return strip;
25973 };
25974 /** @private */
25975 Roo.TabPanel.prototype.createStripList = function(strip){
25976     // div wrapper for retard IE
25977     // returns the "tr" element.
25978     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25979         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25980         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25981     return strip.firstChild.firstChild.firstChild.firstChild;
25982 };
25983 /** @private */
25984 Roo.TabPanel.prototype.createBody = function(container){
25985     var body = document.createElement("div");
25986     Roo.id(body, "tab-body");
25987     Roo.fly(body).addClass("x-tabs-body");
25988     container.appendChild(body);
25989     return body;
25990 };
25991 /** @private */
25992 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25993     var body = Roo.getDom(id);
25994     if(!body){
25995         body = document.createElement("div");
25996         body.id = id;
25997     }
25998     Roo.fly(body).addClass("x-tabs-item-body");
25999     bodyEl.insertBefore(body, bodyEl.firstChild);
26000     return body;
26001 };
26002 /** @private */
26003 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26004     var td = document.createElement("td");
26005     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26006     //stripEl.appendChild(td);
26007     if(closable){
26008         td.className = "x-tabs-closable";
26009         if(!this.closeTpl){
26010             this.closeTpl = new Roo.Template(
26011                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26012                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26013                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26014             );
26015         }
26016         var el = this.closeTpl.overwrite(td, {"text": text});
26017         var close = el.getElementsByTagName("div")[0];
26018         var inner = el.getElementsByTagName("em")[0];
26019         return {"el": el, "close": close, "inner": inner};
26020     } else {
26021         if(!this.tabTpl){
26022             this.tabTpl = new Roo.Template(
26023                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26024                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26025             );
26026         }
26027         var el = this.tabTpl.overwrite(td, {"text": text});
26028         var inner = el.getElementsByTagName("em")[0];
26029         return {"el": el, "inner": inner};
26030     }
26031 };/*
26032  * Based on:
26033  * Ext JS Library 1.1.1
26034  * Copyright(c) 2006-2007, Ext JS, LLC.
26035  *
26036  * Originally Released Under LGPL - original licence link has changed is not relivant.
26037  *
26038  * Fork - LGPL
26039  * <script type="text/javascript">
26040  */
26041
26042 /**
26043  * @class Roo.Button
26044  * @extends Roo.util.Observable
26045  * Simple Button class
26046  * @cfg {String} text The button text
26047  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26048  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26049  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26050  * @cfg {Object} scope The scope of the handler
26051  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26052  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26053  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26054  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26055  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26056  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26057    applies if enableToggle = true)
26058  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26059  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26060   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26061  * @constructor
26062  * Create a new button
26063  * @param {Object} config The config object
26064  */
26065 Roo.Button = function(renderTo, config)
26066 {
26067     if (!config) {
26068         config = renderTo;
26069         renderTo = config.renderTo || false;
26070     }
26071     
26072     Roo.apply(this, config);
26073     this.addEvents({
26074         /**
26075              * @event click
26076              * Fires when this button is clicked
26077              * @param {Button} this
26078              * @param {EventObject} e The click event
26079              */
26080             "click" : true,
26081         /**
26082              * @event toggle
26083              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26084              * @param {Button} this
26085              * @param {Boolean} pressed
26086              */
26087             "toggle" : true,
26088         /**
26089              * @event mouseover
26090              * Fires when the mouse hovers over the button
26091              * @param {Button} this
26092              * @param {Event} e The event object
26093              */
26094         'mouseover' : true,
26095         /**
26096              * @event mouseout
26097              * Fires when the mouse exits the button
26098              * @param {Button} this
26099              * @param {Event} e The event object
26100              */
26101         'mouseout': true,
26102          /**
26103              * @event render
26104              * Fires when the button is rendered
26105              * @param {Button} this
26106              */
26107         'render': true
26108     });
26109     if(this.menu){
26110         this.menu = Roo.menu.MenuMgr.get(this.menu);
26111     }
26112     // register listeners first!!  - so render can be captured..
26113     Roo.util.Observable.call(this);
26114     if(renderTo){
26115         this.render(renderTo);
26116     }
26117     
26118   
26119 };
26120
26121 Roo.extend(Roo.Button, Roo.util.Observable, {
26122     /**
26123      * 
26124      */
26125     
26126     /**
26127      * Read-only. True if this button is hidden
26128      * @type Boolean
26129      */
26130     hidden : false,
26131     /**
26132      * Read-only. True if this button is disabled
26133      * @type Boolean
26134      */
26135     disabled : false,
26136     /**
26137      * Read-only. True if this button is pressed (only if enableToggle = true)
26138      * @type Boolean
26139      */
26140     pressed : false,
26141
26142     /**
26143      * @cfg {Number} tabIndex 
26144      * The DOM tabIndex for this button (defaults to undefined)
26145      */
26146     tabIndex : undefined,
26147
26148     /**
26149      * @cfg {Boolean} enableToggle
26150      * True to enable pressed/not pressed toggling (defaults to false)
26151      */
26152     enableToggle: false,
26153     /**
26154      * @cfg {Mixed} menu
26155      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26156      */
26157     menu : undefined,
26158     /**
26159      * @cfg {String} menuAlign
26160      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26161      */
26162     menuAlign : "tl-bl?",
26163
26164     /**
26165      * @cfg {String} iconCls
26166      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26167      */
26168     iconCls : undefined,
26169     /**
26170      * @cfg {String} type
26171      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26172      */
26173     type : 'button',
26174
26175     // private
26176     menuClassTarget: 'tr',
26177
26178     /**
26179      * @cfg {String} clickEvent
26180      * The type of event to map to the button's event handler (defaults to 'click')
26181      */
26182     clickEvent : 'click',
26183
26184     /**
26185      * @cfg {Boolean} handleMouseEvents
26186      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26187      */
26188     handleMouseEvents : true,
26189
26190     /**
26191      * @cfg {String} tooltipType
26192      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26193      */
26194     tooltipType : 'qtip',
26195
26196     /**
26197      * @cfg {String} cls
26198      * A CSS class to apply to the button's main element.
26199      */
26200     
26201     /**
26202      * @cfg {Roo.Template} template (Optional)
26203      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26204      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26205      * require code modifications if required elements (e.g. a button) aren't present.
26206      */
26207
26208     // private
26209     render : function(renderTo){
26210         var btn;
26211         if(this.hideParent){
26212             this.parentEl = Roo.get(renderTo);
26213         }
26214         if(!this.dhconfig){
26215             if(!this.template){
26216                 if(!Roo.Button.buttonTemplate){
26217                     // hideous table template
26218                     Roo.Button.buttonTemplate = new Roo.Template(
26219                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26220                         '<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>',
26221                         "</tr></tbody></table>");
26222                 }
26223                 this.template = Roo.Button.buttonTemplate;
26224             }
26225             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26226             var btnEl = btn.child("button:first");
26227             btnEl.on('focus', this.onFocus, this);
26228             btnEl.on('blur', this.onBlur, this);
26229             if(this.cls){
26230                 btn.addClass(this.cls);
26231             }
26232             if(this.icon){
26233                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26234             }
26235             if(this.iconCls){
26236                 btnEl.addClass(this.iconCls);
26237                 if(!this.cls){
26238                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26239                 }
26240             }
26241             if(this.tabIndex !== undefined){
26242                 btnEl.dom.tabIndex = this.tabIndex;
26243             }
26244             if(this.tooltip){
26245                 if(typeof this.tooltip == 'object'){
26246                     Roo.QuickTips.tips(Roo.apply({
26247                           target: btnEl.id
26248                     }, this.tooltip));
26249                 } else {
26250                     btnEl.dom[this.tooltipType] = this.tooltip;
26251                 }
26252             }
26253         }else{
26254             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26255         }
26256         this.el = btn;
26257         if(this.id){
26258             this.el.dom.id = this.el.id = this.id;
26259         }
26260         if(this.menu){
26261             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26262             this.menu.on("show", this.onMenuShow, this);
26263             this.menu.on("hide", this.onMenuHide, this);
26264         }
26265         btn.addClass("x-btn");
26266         if(Roo.isIE && !Roo.isIE7){
26267             this.autoWidth.defer(1, this);
26268         }else{
26269             this.autoWidth();
26270         }
26271         if(this.handleMouseEvents){
26272             btn.on("mouseover", this.onMouseOver, this);
26273             btn.on("mouseout", this.onMouseOut, this);
26274             btn.on("mousedown", this.onMouseDown, this);
26275         }
26276         btn.on(this.clickEvent, this.onClick, this);
26277         //btn.on("mouseup", this.onMouseUp, this);
26278         if(this.hidden){
26279             this.hide();
26280         }
26281         if(this.disabled){
26282             this.disable();
26283         }
26284         Roo.ButtonToggleMgr.register(this);
26285         if(this.pressed){
26286             this.el.addClass("x-btn-pressed");
26287         }
26288         if(this.repeat){
26289             var repeater = new Roo.util.ClickRepeater(btn,
26290                 typeof this.repeat == "object" ? this.repeat : {}
26291             );
26292             repeater.on("click", this.onClick,  this);
26293         }
26294         
26295         this.fireEvent('render', this);
26296         
26297     },
26298     /**
26299      * Returns the button's underlying element
26300      * @return {Roo.Element} The element
26301      */
26302     getEl : function(){
26303         return this.el;  
26304     },
26305     
26306     /**
26307      * Destroys this Button and removes any listeners.
26308      */
26309     destroy : function(){
26310         Roo.ButtonToggleMgr.unregister(this);
26311         this.el.removeAllListeners();
26312         this.purgeListeners();
26313         this.el.remove();
26314     },
26315
26316     // private
26317     autoWidth : function(){
26318         if(this.el){
26319             this.el.setWidth("auto");
26320             if(Roo.isIE7 && Roo.isStrict){
26321                 var ib = this.el.child('button');
26322                 if(ib && ib.getWidth() > 20){
26323                     ib.clip();
26324                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26325                 }
26326             }
26327             if(this.minWidth){
26328                 if(this.hidden){
26329                     this.el.beginMeasure();
26330                 }
26331                 if(this.el.getWidth() < this.minWidth){
26332                     this.el.setWidth(this.minWidth);
26333                 }
26334                 if(this.hidden){
26335                     this.el.endMeasure();
26336                 }
26337             }
26338         }
26339     },
26340
26341     /**
26342      * Assigns this button's click handler
26343      * @param {Function} handler The function to call when the button is clicked
26344      * @param {Object} scope (optional) Scope for the function passed in
26345      */
26346     setHandler : function(handler, scope){
26347         this.handler = handler;
26348         this.scope = scope;  
26349     },
26350     
26351     /**
26352      * Sets this button's text
26353      * @param {String} text The button text
26354      */
26355     setText : function(text){
26356         this.text = text;
26357         if(this.el){
26358             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26359         }
26360         this.autoWidth();
26361     },
26362     
26363     /**
26364      * Gets the text for this button
26365      * @return {String} The button text
26366      */
26367     getText : function(){
26368         return this.text;  
26369     },
26370     
26371     /**
26372      * Show this button
26373      */
26374     show: function(){
26375         this.hidden = false;
26376         if(this.el){
26377             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26378         }
26379     },
26380     
26381     /**
26382      * Hide this button
26383      */
26384     hide: function(){
26385         this.hidden = true;
26386         if(this.el){
26387             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26388         }
26389     },
26390     
26391     /**
26392      * Convenience function for boolean show/hide
26393      * @param {Boolean} visible True to show, false to hide
26394      */
26395     setVisible: function(visible){
26396         if(visible) {
26397             this.show();
26398         }else{
26399             this.hide();
26400         }
26401     },
26402     
26403     /**
26404      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26405      * @param {Boolean} state (optional) Force a particular state
26406      */
26407     toggle : function(state){
26408         state = state === undefined ? !this.pressed : state;
26409         if(state != this.pressed){
26410             if(state){
26411                 this.el.addClass("x-btn-pressed");
26412                 this.pressed = true;
26413                 this.fireEvent("toggle", this, true);
26414             }else{
26415                 this.el.removeClass("x-btn-pressed");
26416                 this.pressed = false;
26417                 this.fireEvent("toggle", this, false);
26418             }
26419             if(this.toggleHandler){
26420                 this.toggleHandler.call(this.scope || this, this, state);
26421             }
26422         }
26423     },
26424     
26425     /**
26426      * Focus the button
26427      */
26428     focus : function(){
26429         this.el.child('button:first').focus();
26430     },
26431     
26432     /**
26433      * Disable this button
26434      */
26435     disable : function(){
26436         if(this.el){
26437             this.el.addClass("x-btn-disabled");
26438         }
26439         this.disabled = true;
26440     },
26441     
26442     /**
26443      * Enable this button
26444      */
26445     enable : function(){
26446         if(this.el){
26447             this.el.removeClass("x-btn-disabled");
26448         }
26449         this.disabled = false;
26450     },
26451
26452     /**
26453      * Convenience function for boolean enable/disable
26454      * @param {Boolean} enabled True to enable, false to disable
26455      */
26456     setDisabled : function(v){
26457         this[v !== true ? "enable" : "disable"]();
26458     },
26459
26460     // private
26461     onClick : function(e){
26462         if(e){
26463             e.preventDefault();
26464         }
26465         if(e.button != 0){
26466             return;
26467         }
26468         if(!this.disabled){
26469             if(this.enableToggle){
26470                 this.toggle();
26471             }
26472             if(this.menu && !this.menu.isVisible()){
26473                 this.menu.show(this.el, this.menuAlign);
26474             }
26475             this.fireEvent("click", this, e);
26476             if(this.handler){
26477                 this.el.removeClass("x-btn-over");
26478                 this.handler.call(this.scope || this, this, e);
26479             }
26480         }
26481     },
26482     // private
26483     onMouseOver : function(e){
26484         if(!this.disabled){
26485             this.el.addClass("x-btn-over");
26486             this.fireEvent('mouseover', this, e);
26487         }
26488     },
26489     // private
26490     onMouseOut : function(e){
26491         if(!e.within(this.el,  true)){
26492             this.el.removeClass("x-btn-over");
26493             this.fireEvent('mouseout', this, e);
26494         }
26495     },
26496     // private
26497     onFocus : function(e){
26498         if(!this.disabled){
26499             this.el.addClass("x-btn-focus");
26500         }
26501     },
26502     // private
26503     onBlur : function(e){
26504         this.el.removeClass("x-btn-focus");
26505     },
26506     // private
26507     onMouseDown : function(e){
26508         if(!this.disabled && e.button == 0){
26509             this.el.addClass("x-btn-click");
26510             Roo.get(document).on('mouseup', this.onMouseUp, this);
26511         }
26512     },
26513     // private
26514     onMouseUp : function(e){
26515         if(e.button == 0){
26516             this.el.removeClass("x-btn-click");
26517             Roo.get(document).un('mouseup', this.onMouseUp, this);
26518         }
26519     },
26520     // private
26521     onMenuShow : function(e){
26522         this.el.addClass("x-btn-menu-active");
26523     },
26524     // private
26525     onMenuHide : function(e){
26526         this.el.removeClass("x-btn-menu-active");
26527     }   
26528 });
26529
26530 // Private utility class used by Button
26531 Roo.ButtonToggleMgr = function(){
26532    var groups = {};
26533    
26534    function toggleGroup(btn, state){
26535        if(state){
26536            var g = groups[btn.toggleGroup];
26537            for(var i = 0, l = g.length; i < l; i++){
26538                if(g[i] != btn){
26539                    g[i].toggle(false);
26540                }
26541            }
26542        }
26543    }
26544    
26545    return {
26546        register : function(btn){
26547            if(!btn.toggleGroup){
26548                return;
26549            }
26550            var g = groups[btn.toggleGroup];
26551            if(!g){
26552                g = groups[btn.toggleGroup] = [];
26553            }
26554            g.push(btn);
26555            btn.on("toggle", toggleGroup);
26556        },
26557        
26558        unregister : function(btn){
26559            if(!btn.toggleGroup){
26560                return;
26561            }
26562            var g = groups[btn.toggleGroup];
26563            if(g){
26564                g.remove(btn);
26565                btn.un("toggle", toggleGroup);
26566            }
26567        }
26568    };
26569 }();/*
26570  * Based on:
26571  * Ext JS Library 1.1.1
26572  * Copyright(c) 2006-2007, Ext JS, LLC.
26573  *
26574  * Originally Released Under LGPL - original licence link has changed is not relivant.
26575  *
26576  * Fork - LGPL
26577  * <script type="text/javascript">
26578  */
26579  
26580 /**
26581  * @class Roo.SplitButton
26582  * @extends Roo.Button
26583  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26584  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26585  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26586  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26587  * @cfg {String} arrowTooltip The title attribute of the arrow
26588  * @constructor
26589  * Create a new menu button
26590  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26591  * @param {Object} config The config object
26592  */
26593 Roo.SplitButton = function(renderTo, config){
26594     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26595     /**
26596      * @event arrowclick
26597      * Fires when this button's arrow is clicked
26598      * @param {SplitButton} this
26599      * @param {EventObject} e The click event
26600      */
26601     this.addEvents({"arrowclick":true});
26602 };
26603
26604 Roo.extend(Roo.SplitButton, Roo.Button, {
26605     render : function(renderTo){
26606         // this is one sweet looking template!
26607         var tpl = new Roo.Template(
26608             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26609             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26610             '<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>',
26611             "</tbody></table></td><td>",
26612             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26613             '<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>',
26614             "</tbody></table></td></tr></table>"
26615         );
26616         var btn = tpl.append(renderTo, [this.text, this.type], true);
26617         var btnEl = btn.child("button");
26618         if(this.cls){
26619             btn.addClass(this.cls);
26620         }
26621         if(this.icon){
26622             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26623         }
26624         if(this.iconCls){
26625             btnEl.addClass(this.iconCls);
26626             if(!this.cls){
26627                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26628             }
26629         }
26630         this.el = btn;
26631         if(this.handleMouseEvents){
26632             btn.on("mouseover", this.onMouseOver, this);
26633             btn.on("mouseout", this.onMouseOut, this);
26634             btn.on("mousedown", this.onMouseDown, this);
26635             btn.on("mouseup", this.onMouseUp, this);
26636         }
26637         btn.on(this.clickEvent, this.onClick, this);
26638         if(this.tooltip){
26639             if(typeof this.tooltip == 'object'){
26640                 Roo.QuickTips.tips(Roo.apply({
26641                       target: btnEl.id
26642                 }, this.tooltip));
26643             } else {
26644                 btnEl.dom[this.tooltipType] = this.tooltip;
26645             }
26646         }
26647         if(this.arrowTooltip){
26648             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26649         }
26650         if(this.hidden){
26651             this.hide();
26652         }
26653         if(this.disabled){
26654             this.disable();
26655         }
26656         if(this.pressed){
26657             this.el.addClass("x-btn-pressed");
26658         }
26659         if(Roo.isIE && !Roo.isIE7){
26660             this.autoWidth.defer(1, this);
26661         }else{
26662             this.autoWidth();
26663         }
26664         if(this.menu){
26665             this.menu.on("show", this.onMenuShow, this);
26666             this.menu.on("hide", this.onMenuHide, this);
26667         }
26668         this.fireEvent('render', this);
26669     },
26670
26671     // private
26672     autoWidth : function(){
26673         if(this.el){
26674             var tbl = this.el.child("table:first");
26675             var tbl2 = this.el.child("table:last");
26676             this.el.setWidth("auto");
26677             tbl.setWidth("auto");
26678             if(Roo.isIE7 && Roo.isStrict){
26679                 var ib = this.el.child('button:first');
26680                 if(ib && ib.getWidth() > 20){
26681                     ib.clip();
26682                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26683                 }
26684             }
26685             if(this.minWidth){
26686                 if(this.hidden){
26687                     this.el.beginMeasure();
26688                 }
26689                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26690                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26691                 }
26692                 if(this.hidden){
26693                     this.el.endMeasure();
26694                 }
26695             }
26696             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26697         } 
26698     },
26699     /**
26700      * Sets this button's click handler
26701      * @param {Function} handler The function to call when the button is clicked
26702      * @param {Object} scope (optional) Scope for the function passed above
26703      */
26704     setHandler : function(handler, scope){
26705         this.handler = handler;
26706         this.scope = scope;  
26707     },
26708     
26709     /**
26710      * Sets this button's arrow click handler
26711      * @param {Function} handler The function to call when the arrow is clicked
26712      * @param {Object} scope (optional) Scope for the function passed above
26713      */
26714     setArrowHandler : function(handler, scope){
26715         this.arrowHandler = handler;
26716         this.scope = scope;  
26717     },
26718     
26719     /**
26720      * Focus the button
26721      */
26722     focus : function(){
26723         if(this.el){
26724             this.el.child("button:first").focus();
26725         }
26726     },
26727
26728     // private
26729     onClick : function(e){
26730         e.preventDefault();
26731         if(!this.disabled){
26732             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26733                 if(this.menu && !this.menu.isVisible()){
26734                     this.menu.show(this.el, this.menuAlign);
26735                 }
26736                 this.fireEvent("arrowclick", this, e);
26737                 if(this.arrowHandler){
26738                     this.arrowHandler.call(this.scope || this, this, e);
26739                 }
26740             }else{
26741                 this.fireEvent("click", this, e);
26742                 if(this.handler){
26743                     this.handler.call(this.scope || this, this, e);
26744                 }
26745             }
26746         }
26747     },
26748     // private
26749     onMouseDown : function(e){
26750         if(!this.disabled){
26751             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26752         }
26753     },
26754     // private
26755     onMouseUp : function(e){
26756         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26757     }   
26758 });
26759
26760
26761 // backwards compat
26762 Roo.MenuButton = Roo.SplitButton;/*
26763  * Based on:
26764  * Ext JS Library 1.1.1
26765  * Copyright(c) 2006-2007, Ext JS, LLC.
26766  *
26767  * Originally Released Under LGPL - original licence link has changed is not relivant.
26768  *
26769  * Fork - LGPL
26770  * <script type="text/javascript">
26771  */
26772
26773 /**
26774  * @class Roo.Toolbar
26775  * Basic Toolbar class.
26776  * @constructor
26777  * Creates a new Toolbar
26778  * @param {Object} container The config object
26779  */ 
26780 Roo.Toolbar = function(container, buttons, config)
26781 {
26782     /// old consturctor format still supported..
26783     if(container instanceof Array){ // omit the container for later rendering
26784         buttons = container;
26785         config = buttons;
26786         container = null;
26787     }
26788     if (typeof(container) == 'object' && container.xtype) {
26789         config = container;
26790         container = config.container;
26791         buttons = config.buttons || []; // not really - use items!!
26792     }
26793     var xitems = [];
26794     if (config && config.items) {
26795         xitems = config.items;
26796         delete config.items;
26797     }
26798     Roo.apply(this, config);
26799     this.buttons = buttons;
26800     
26801     if(container){
26802         this.render(container);
26803     }
26804     this.xitems = xitems;
26805     Roo.each(xitems, function(b) {
26806         this.add(b);
26807     }, this);
26808     
26809 };
26810
26811 Roo.Toolbar.prototype = {
26812     /**
26813      * @cfg {Array} items
26814      * array of button configs or elements to add (will be converted to a MixedCollection)
26815      */
26816     
26817     /**
26818      * @cfg {String/HTMLElement/Element} container
26819      * The id or element that will contain the toolbar
26820      */
26821     // private
26822     render : function(ct){
26823         this.el = Roo.get(ct);
26824         if(this.cls){
26825             this.el.addClass(this.cls);
26826         }
26827         // using a table allows for vertical alignment
26828         // 100% width is needed by Safari...
26829         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26830         this.tr = this.el.child("tr", true);
26831         var autoId = 0;
26832         this.items = new Roo.util.MixedCollection(false, function(o){
26833             return o.id || ("item" + (++autoId));
26834         });
26835         if(this.buttons){
26836             this.add.apply(this, this.buttons);
26837             delete this.buttons;
26838         }
26839     },
26840
26841     /**
26842      * Adds element(s) to the toolbar -- this function takes a variable number of 
26843      * arguments of mixed type and adds them to the toolbar.
26844      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26845      * <ul>
26846      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26847      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26848      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26849      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26850      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26851      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26852      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26853      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26854      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26855      * </ul>
26856      * @param {Mixed} arg2
26857      * @param {Mixed} etc.
26858      */
26859     add : function(){
26860         var a = arguments, l = a.length;
26861         for(var i = 0; i < l; i++){
26862             this._add(a[i]);
26863         }
26864     },
26865     // private..
26866     _add : function(el) {
26867         
26868         if (el.xtype) {
26869             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26870         }
26871         
26872         if (el.applyTo){ // some kind of form field
26873             return this.addField(el);
26874         } 
26875         if (el.render){ // some kind of Toolbar.Item
26876             return this.addItem(el);
26877         }
26878         if (typeof el == "string"){ // string
26879             if(el == "separator" || el == "-"){
26880                 return this.addSeparator();
26881             }
26882             if (el == " "){
26883                 return this.addSpacer();
26884             }
26885             if(el == "->"){
26886                 return this.addFill();
26887             }
26888             return this.addText(el);
26889             
26890         }
26891         if(el.tagName){ // element
26892             return this.addElement(el);
26893         }
26894         if(typeof el == "object"){ // must be button config?
26895             return this.addButton(el);
26896         }
26897         // and now what?!?!
26898         return false;
26899         
26900     },
26901     
26902     /**
26903      * Add an Xtype element
26904      * @param {Object} xtype Xtype Object
26905      * @return {Object} created Object
26906      */
26907     addxtype : function(e){
26908         return this.add(e);  
26909     },
26910     
26911     /**
26912      * Returns the Element for this toolbar.
26913      * @return {Roo.Element}
26914      */
26915     getEl : function(){
26916         return this.el;  
26917     },
26918     
26919     /**
26920      * Adds a separator
26921      * @return {Roo.Toolbar.Item} The separator item
26922      */
26923     addSeparator : function(){
26924         return this.addItem(new Roo.Toolbar.Separator());
26925     },
26926
26927     /**
26928      * Adds a spacer element
26929      * @return {Roo.Toolbar.Spacer} The spacer item
26930      */
26931     addSpacer : function(){
26932         return this.addItem(new Roo.Toolbar.Spacer());
26933     },
26934
26935     /**
26936      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26937      * @return {Roo.Toolbar.Fill} The fill item
26938      */
26939     addFill : function(){
26940         return this.addItem(new Roo.Toolbar.Fill());
26941     },
26942
26943     /**
26944      * Adds any standard HTML element to the toolbar
26945      * @param {String/HTMLElement/Element} el The element or id of the element to add
26946      * @return {Roo.Toolbar.Item} The element's item
26947      */
26948     addElement : function(el){
26949         return this.addItem(new Roo.Toolbar.Item(el));
26950     },
26951     /**
26952      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26953      * @type Roo.util.MixedCollection  
26954      */
26955     items : false,
26956      
26957     /**
26958      * Adds any Toolbar.Item or subclass
26959      * @param {Roo.Toolbar.Item} item
26960      * @return {Roo.Toolbar.Item} The item
26961      */
26962     addItem : function(item){
26963         var td = this.nextBlock();
26964         item.render(td);
26965         this.items.add(item);
26966         return item;
26967     },
26968     
26969     /**
26970      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26971      * @param {Object/Array} config A button config or array of configs
26972      * @return {Roo.Toolbar.Button/Array}
26973      */
26974     addButton : function(config){
26975         if(config instanceof Array){
26976             var buttons = [];
26977             for(var i = 0, len = config.length; i < len; i++) {
26978                 buttons.push(this.addButton(config[i]));
26979             }
26980             return buttons;
26981         }
26982         var b = config;
26983         if(!(config instanceof Roo.Toolbar.Button)){
26984             b = config.split ?
26985                 new Roo.Toolbar.SplitButton(config) :
26986                 new Roo.Toolbar.Button(config);
26987         }
26988         var td = this.nextBlock();
26989         b.render(td);
26990         this.items.add(b);
26991         return b;
26992     },
26993     
26994     /**
26995      * Adds text to the toolbar
26996      * @param {String} text The text to add
26997      * @return {Roo.Toolbar.Item} The element's item
26998      */
26999     addText : function(text){
27000         return this.addItem(new Roo.Toolbar.TextItem(text));
27001     },
27002     
27003     /**
27004      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27005      * @param {Number} index The index where the item is to be inserted
27006      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27007      * @return {Roo.Toolbar.Button/Item}
27008      */
27009     insertButton : function(index, item){
27010         if(item instanceof Array){
27011             var buttons = [];
27012             for(var i = 0, len = item.length; i < len; i++) {
27013                buttons.push(this.insertButton(index + i, item[i]));
27014             }
27015             return buttons;
27016         }
27017         if (!(item instanceof Roo.Toolbar.Button)){
27018            item = new Roo.Toolbar.Button(item);
27019         }
27020         var td = document.createElement("td");
27021         this.tr.insertBefore(td, this.tr.childNodes[index]);
27022         item.render(td);
27023         this.items.insert(index, item);
27024         return item;
27025     },
27026     
27027     /**
27028      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27029      * @param {Object} config
27030      * @return {Roo.Toolbar.Item} The element's item
27031      */
27032     addDom : function(config, returnEl){
27033         var td = this.nextBlock();
27034         Roo.DomHelper.overwrite(td, config);
27035         var ti = new Roo.Toolbar.Item(td.firstChild);
27036         ti.render(td);
27037         this.items.add(ti);
27038         return ti;
27039     },
27040
27041     /**
27042      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27043      * @type Roo.util.MixedCollection  
27044      */
27045     fields : false,
27046     
27047     /**
27048      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27049      * Note: the field should not have been rendered yet. For a field that has already been
27050      * rendered, use {@link #addElement}.
27051      * @param {Roo.form.Field} field
27052      * @return {Roo.ToolbarItem}
27053      */
27054      
27055       
27056     addField : function(field) {
27057         if (!this.fields) {
27058             var autoId = 0;
27059             this.fields = new Roo.util.MixedCollection(false, function(o){
27060                 return o.id || ("item" + (++autoId));
27061             });
27062
27063         }
27064         
27065         var td = this.nextBlock();
27066         field.render(td);
27067         var ti = new Roo.Toolbar.Item(td.firstChild);
27068         ti.render(td);
27069         this.items.add(ti);
27070         this.fields.add(field);
27071         return ti;
27072     },
27073     /**
27074      * Hide the toolbar
27075      * @method hide
27076      */
27077      
27078       
27079     hide : function()
27080     {
27081         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27082         this.el.child('div').hide();
27083     },
27084     /**
27085      * Show the toolbar
27086      * @method show
27087      */
27088     show : function()
27089     {
27090         this.el.child('div').show();
27091     },
27092       
27093     // private
27094     nextBlock : function(){
27095         var td = document.createElement("td");
27096         this.tr.appendChild(td);
27097         return td;
27098     },
27099
27100     // private
27101     destroy : function(){
27102         if(this.items){ // rendered?
27103             Roo.destroy.apply(Roo, this.items.items);
27104         }
27105         if(this.fields){ // rendered?
27106             Roo.destroy.apply(Roo, this.fields.items);
27107         }
27108         Roo.Element.uncache(this.el, this.tr);
27109     }
27110 };
27111
27112 /**
27113  * @class Roo.Toolbar.Item
27114  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27115  * @constructor
27116  * Creates a new Item
27117  * @param {HTMLElement} el 
27118  */
27119 Roo.Toolbar.Item = function(el){
27120     this.el = Roo.getDom(el);
27121     this.id = Roo.id(this.el);
27122     this.hidden = false;
27123 };
27124
27125 Roo.Toolbar.Item.prototype = {
27126     
27127     /**
27128      * Get this item's HTML Element
27129      * @return {HTMLElement}
27130      */
27131     getEl : function(){
27132        return this.el;  
27133     },
27134
27135     // private
27136     render : function(td){
27137         this.td = td;
27138         td.appendChild(this.el);
27139     },
27140     
27141     /**
27142      * Removes and destroys this item.
27143      */
27144     destroy : function(){
27145         this.td.parentNode.removeChild(this.td);
27146     },
27147     
27148     /**
27149      * Shows this item.
27150      */
27151     show: function(){
27152         this.hidden = false;
27153         this.td.style.display = "";
27154     },
27155     
27156     /**
27157      * Hides this item.
27158      */
27159     hide: function(){
27160         this.hidden = true;
27161         this.td.style.display = "none";
27162     },
27163     
27164     /**
27165      * Convenience function for boolean show/hide.
27166      * @param {Boolean} visible true to show/false to hide
27167      */
27168     setVisible: function(visible){
27169         if(visible) {
27170             this.show();
27171         }else{
27172             this.hide();
27173         }
27174     },
27175     
27176     /**
27177      * Try to focus this item.
27178      */
27179     focus : function(){
27180         Roo.fly(this.el).focus();
27181     },
27182     
27183     /**
27184      * Disables this item.
27185      */
27186     disable : function(){
27187         Roo.fly(this.td).addClass("x-item-disabled");
27188         this.disabled = true;
27189         this.el.disabled = true;
27190     },
27191     
27192     /**
27193      * Enables this item.
27194      */
27195     enable : function(){
27196         Roo.fly(this.td).removeClass("x-item-disabled");
27197         this.disabled = false;
27198         this.el.disabled = false;
27199     }
27200 };
27201
27202
27203 /**
27204  * @class Roo.Toolbar.Separator
27205  * @extends Roo.Toolbar.Item
27206  * A simple toolbar separator class
27207  * @constructor
27208  * Creates a new Separator
27209  */
27210 Roo.Toolbar.Separator = function(){
27211     var s = document.createElement("span");
27212     s.className = "ytb-sep";
27213     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27214 };
27215 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27216     enable:Roo.emptyFn,
27217     disable:Roo.emptyFn,
27218     focus:Roo.emptyFn
27219 });
27220
27221 /**
27222  * @class Roo.Toolbar.Spacer
27223  * @extends Roo.Toolbar.Item
27224  * A simple element that adds extra horizontal space to a toolbar.
27225  * @constructor
27226  * Creates a new Spacer
27227  */
27228 Roo.Toolbar.Spacer = function(){
27229     var s = document.createElement("div");
27230     s.className = "ytb-spacer";
27231     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27232 };
27233 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27234     enable:Roo.emptyFn,
27235     disable:Roo.emptyFn,
27236     focus:Roo.emptyFn
27237 });
27238
27239 /**
27240  * @class Roo.Toolbar.Fill
27241  * @extends Roo.Toolbar.Spacer
27242  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27243  * @constructor
27244  * Creates a new Spacer
27245  */
27246 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27247     // private
27248     render : function(td){
27249         td.style.width = '100%';
27250         Roo.Toolbar.Fill.superclass.render.call(this, td);
27251     }
27252 });
27253
27254 /**
27255  * @class Roo.Toolbar.TextItem
27256  * @extends Roo.Toolbar.Item
27257  * A simple class that renders text directly into a toolbar.
27258  * @constructor
27259  * Creates a new TextItem
27260  * @param {String} text
27261  */
27262 Roo.Toolbar.TextItem = function(text){
27263     if (typeof(text) == 'object') {
27264         text = text.text;
27265     }
27266     var s = document.createElement("span");
27267     s.className = "ytb-text";
27268     s.innerHTML = text;
27269     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27270 };
27271 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27272     enable:Roo.emptyFn,
27273     disable:Roo.emptyFn,
27274     focus:Roo.emptyFn
27275 });
27276
27277 /**
27278  * @class Roo.Toolbar.Button
27279  * @extends Roo.Button
27280  * A button that renders into a toolbar.
27281  * @constructor
27282  * Creates a new Button
27283  * @param {Object} config A standard {@link Roo.Button} config object
27284  */
27285 Roo.Toolbar.Button = function(config){
27286     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27287 };
27288 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27289     render : function(td){
27290         this.td = td;
27291         Roo.Toolbar.Button.superclass.render.call(this, td);
27292     },
27293     
27294     /**
27295      * Removes and destroys this button
27296      */
27297     destroy : function(){
27298         Roo.Toolbar.Button.superclass.destroy.call(this);
27299         this.td.parentNode.removeChild(this.td);
27300     },
27301     
27302     /**
27303      * Shows this button
27304      */
27305     show: function(){
27306         this.hidden = false;
27307         this.td.style.display = "";
27308     },
27309     
27310     /**
27311      * Hides this button
27312      */
27313     hide: function(){
27314         this.hidden = true;
27315         this.td.style.display = "none";
27316     },
27317
27318     /**
27319      * Disables this item
27320      */
27321     disable : function(){
27322         Roo.fly(this.td).addClass("x-item-disabled");
27323         this.disabled = true;
27324     },
27325
27326     /**
27327      * Enables this item
27328      */
27329     enable : function(){
27330         Roo.fly(this.td).removeClass("x-item-disabled");
27331         this.disabled = false;
27332     }
27333 });
27334 // backwards compat
27335 Roo.ToolbarButton = Roo.Toolbar.Button;
27336
27337 /**
27338  * @class Roo.Toolbar.SplitButton
27339  * @extends Roo.SplitButton
27340  * A menu button that renders into a toolbar.
27341  * @constructor
27342  * Creates a new SplitButton
27343  * @param {Object} config A standard {@link Roo.SplitButton} config object
27344  */
27345 Roo.Toolbar.SplitButton = function(config){
27346     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27347 };
27348 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27349     render : function(td){
27350         this.td = td;
27351         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27352     },
27353     
27354     /**
27355      * Removes and destroys this button
27356      */
27357     destroy : function(){
27358         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27359         this.td.parentNode.removeChild(this.td);
27360     },
27361     
27362     /**
27363      * Shows this button
27364      */
27365     show: function(){
27366         this.hidden = false;
27367         this.td.style.display = "";
27368     },
27369     
27370     /**
27371      * Hides this button
27372      */
27373     hide: function(){
27374         this.hidden = true;
27375         this.td.style.display = "none";
27376     }
27377 });
27378
27379 // backwards compat
27380 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27381  * Based on:
27382  * Ext JS Library 1.1.1
27383  * Copyright(c) 2006-2007, Ext JS, LLC.
27384  *
27385  * Originally Released Under LGPL - original licence link has changed is not relivant.
27386  *
27387  * Fork - LGPL
27388  * <script type="text/javascript">
27389  */
27390  
27391 /**
27392  * @class Roo.PagingToolbar
27393  * @extends Roo.Toolbar
27394  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27395  * @constructor
27396  * Create a new PagingToolbar
27397  * @param {Object} config The config object
27398  */
27399 Roo.PagingToolbar = function(el, ds, config)
27400 {
27401     // old args format still supported... - xtype is prefered..
27402     if (typeof(el) == 'object' && el.xtype) {
27403         // created from xtype...
27404         config = el;
27405         ds = el.dataSource;
27406         el = config.container;
27407     }
27408     var items = [];
27409     if (config.items) {
27410         items = config.items;
27411         config.items = [];
27412     }
27413     
27414     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27415     this.ds = ds;
27416     this.cursor = 0;
27417     this.renderButtons(this.el);
27418     this.bind(ds);
27419     
27420     // supprot items array.
27421    
27422     Roo.each(items, function(e) {
27423         this.add(Roo.factory(e));
27424     },this);
27425     
27426 };
27427
27428 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27429     /**
27430      * @cfg {Roo.data.Store} dataSource
27431      * The underlying data store providing the paged data
27432      */
27433     /**
27434      * @cfg {String/HTMLElement/Element} container
27435      * container The id or element that will contain the toolbar
27436      */
27437     /**
27438      * @cfg {Boolean} displayInfo
27439      * True to display the displayMsg (defaults to false)
27440      */
27441     /**
27442      * @cfg {Number} pageSize
27443      * The number of records to display per page (defaults to 20)
27444      */
27445     pageSize: 20,
27446     /**
27447      * @cfg {String} displayMsg
27448      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27449      */
27450     displayMsg : 'Displaying {0} - {1} of {2}',
27451     /**
27452      * @cfg {String} emptyMsg
27453      * The message to display when no records are found (defaults to "No data to display")
27454      */
27455     emptyMsg : 'No data to display',
27456     /**
27457      * Customizable piece of the default paging text (defaults to "Page")
27458      * @type String
27459      */
27460     beforePageText : "Page",
27461     /**
27462      * Customizable piece of the default paging text (defaults to "of %0")
27463      * @type String
27464      */
27465     afterPageText : "of {0}",
27466     /**
27467      * Customizable piece of the default paging text (defaults to "First Page")
27468      * @type String
27469      */
27470     firstText : "First Page",
27471     /**
27472      * Customizable piece of the default paging text (defaults to "Previous Page")
27473      * @type String
27474      */
27475     prevText : "Previous Page",
27476     /**
27477      * Customizable piece of the default paging text (defaults to "Next Page")
27478      * @type String
27479      */
27480     nextText : "Next Page",
27481     /**
27482      * Customizable piece of the default paging text (defaults to "Last Page")
27483      * @type String
27484      */
27485     lastText : "Last Page",
27486     /**
27487      * Customizable piece of the default paging text (defaults to "Refresh")
27488      * @type String
27489      */
27490     refreshText : "Refresh",
27491
27492     // private
27493     renderButtons : function(el){
27494         Roo.PagingToolbar.superclass.render.call(this, el);
27495         this.first = this.addButton({
27496             tooltip: this.firstText,
27497             cls: "x-btn-icon x-grid-page-first",
27498             disabled: true,
27499             handler: this.onClick.createDelegate(this, ["first"])
27500         });
27501         this.prev = this.addButton({
27502             tooltip: this.prevText,
27503             cls: "x-btn-icon x-grid-page-prev",
27504             disabled: true,
27505             handler: this.onClick.createDelegate(this, ["prev"])
27506         });
27507         //this.addSeparator();
27508         this.add(this.beforePageText);
27509         this.field = Roo.get(this.addDom({
27510            tag: "input",
27511            type: "text",
27512            size: "3",
27513            value: "1",
27514            cls: "x-grid-page-number"
27515         }).el);
27516         this.field.on("keydown", this.onPagingKeydown, this);
27517         this.field.on("focus", function(){this.dom.select();});
27518         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27519         this.field.setHeight(18);
27520         //this.addSeparator();
27521         this.next = this.addButton({
27522             tooltip: this.nextText,
27523             cls: "x-btn-icon x-grid-page-next",
27524             disabled: true,
27525             handler: this.onClick.createDelegate(this, ["next"])
27526         });
27527         this.last = this.addButton({
27528             tooltip: this.lastText,
27529             cls: "x-btn-icon x-grid-page-last",
27530             disabled: true,
27531             handler: this.onClick.createDelegate(this, ["last"])
27532         });
27533         //this.addSeparator();
27534         this.loading = this.addButton({
27535             tooltip: this.refreshText,
27536             cls: "x-btn-icon x-grid-loading",
27537             handler: this.onClick.createDelegate(this, ["refresh"])
27538         });
27539
27540         if(this.displayInfo){
27541             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27542         }
27543     },
27544
27545     // private
27546     updateInfo : function(){
27547         if(this.displayEl){
27548             var count = this.ds.getCount();
27549             var msg = count == 0 ?
27550                 this.emptyMsg :
27551                 String.format(
27552                     this.displayMsg,
27553                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27554                 );
27555             this.displayEl.update(msg);
27556         }
27557     },
27558
27559     // private
27560     onLoad : function(ds, r, o){
27561        this.cursor = o.params ? o.params.start : 0;
27562        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27563
27564        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27565        this.field.dom.value = ap;
27566        this.first.setDisabled(ap == 1);
27567        this.prev.setDisabled(ap == 1);
27568        this.next.setDisabled(ap == ps);
27569        this.last.setDisabled(ap == ps);
27570        this.loading.enable();
27571        this.updateInfo();
27572     },
27573
27574     // private
27575     getPageData : function(){
27576         var total = this.ds.getTotalCount();
27577         return {
27578             total : total,
27579             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27580             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27581         };
27582     },
27583
27584     // private
27585     onLoadError : function(){
27586         this.loading.enable();
27587     },
27588
27589     // private
27590     onPagingKeydown : function(e){
27591         var k = e.getKey();
27592         var d = this.getPageData();
27593         if(k == e.RETURN){
27594             var v = this.field.dom.value, pageNum;
27595             if(!v || isNaN(pageNum = parseInt(v, 10))){
27596                 this.field.dom.value = d.activePage;
27597                 return;
27598             }
27599             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27600             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27601             e.stopEvent();
27602         }
27603         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))
27604         {
27605           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27606           this.field.dom.value = pageNum;
27607           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27608           e.stopEvent();
27609         }
27610         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27611         {
27612           var v = this.field.dom.value, pageNum; 
27613           var increment = (e.shiftKey) ? 10 : 1;
27614           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27615             increment *= -1;
27616           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27617             this.field.dom.value = d.activePage;
27618             return;
27619           }
27620           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27621           {
27622             this.field.dom.value = parseInt(v, 10) + increment;
27623             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27624             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27625           }
27626           e.stopEvent();
27627         }
27628     },
27629
27630     // private
27631     beforeLoad : function(){
27632         if(this.loading){
27633             this.loading.disable();
27634         }
27635     },
27636
27637     // private
27638     onClick : function(which){
27639         var ds = this.ds;
27640         switch(which){
27641             case "first":
27642                 ds.load({params:{start: 0, limit: this.pageSize}});
27643             break;
27644             case "prev":
27645                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27646             break;
27647             case "next":
27648                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27649             break;
27650             case "last":
27651                 var total = ds.getTotalCount();
27652                 var extra = total % this.pageSize;
27653                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27654                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27655             break;
27656             case "refresh":
27657                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27658             break;
27659         }
27660     },
27661
27662     /**
27663      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27664      * @param {Roo.data.Store} store The data store to unbind
27665      */
27666     unbind : function(ds){
27667         ds.un("beforeload", this.beforeLoad, this);
27668         ds.un("load", this.onLoad, this);
27669         ds.un("loadexception", this.onLoadError, this);
27670         ds.un("remove", this.updateInfo, this);
27671         ds.un("add", this.updateInfo, this);
27672         this.ds = undefined;
27673     },
27674
27675     /**
27676      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27677      * @param {Roo.data.Store} store The data store to bind
27678      */
27679     bind : function(ds){
27680         ds.on("beforeload", this.beforeLoad, this);
27681         ds.on("load", this.onLoad, this);
27682         ds.on("loadexception", this.onLoadError, this);
27683         ds.on("remove", this.updateInfo, this);
27684         ds.on("add", this.updateInfo, this);
27685         this.ds = ds;
27686     }
27687 });/*
27688  * Based on:
27689  * Ext JS Library 1.1.1
27690  * Copyright(c) 2006-2007, Ext JS, LLC.
27691  *
27692  * Originally Released Under LGPL - original licence link has changed is not relivant.
27693  *
27694  * Fork - LGPL
27695  * <script type="text/javascript">
27696  */
27697
27698 /**
27699  * @class Roo.Resizable
27700  * @extends Roo.util.Observable
27701  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27702  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27703  * 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
27704  * the element will be wrapped for you automatically.</p>
27705  * <p>Here is the list of valid resize handles:</p>
27706  * <pre>
27707 Value   Description
27708 ------  -------------------
27709  'n'     north
27710  's'     south
27711  'e'     east
27712  'w'     west
27713  'nw'    northwest
27714  'sw'    southwest
27715  'se'    southeast
27716  'ne'    northeast
27717  'hd'    horizontal drag
27718  'all'   all
27719 </pre>
27720  * <p>Here's an example showing the creation of a typical Resizable:</p>
27721  * <pre><code>
27722 var resizer = new Roo.Resizable("element-id", {
27723     handles: 'all',
27724     minWidth: 200,
27725     minHeight: 100,
27726     maxWidth: 500,
27727     maxHeight: 400,
27728     pinned: true
27729 });
27730 resizer.on("resize", myHandler);
27731 </code></pre>
27732  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27733  * resizer.east.setDisplayed(false);</p>
27734  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27735  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27736  * resize operation's new size (defaults to [0, 0])
27737  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27738  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27739  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27740  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27741  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27742  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27743  * @cfg {Number} width The width of the element in pixels (defaults to null)
27744  * @cfg {Number} height The height of the element in pixels (defaults to null)
27745  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27746  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27747  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27748  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27749  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27750  * in favor of the handles config option (defaults to false)
27751  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27752  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27753  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27754  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27755  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27756  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27757  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27758  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27759  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27760  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27761  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27762  * @constructor
27763  * Create a new resizable component
27764  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27765  * @param {Object} config configuration options
27766   */
27767 Roo.Resizable = function(el, config)
27768 {
27769     this.el = Roo.get(el);
27770
27771     if(config && config.wrap){
27772         config.resizeChild = this.el;
27773         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27774         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27775         this.el.setStyle("overflow", "hidden");
27776         this.el.setPositioning(config.resizeChild.getPositioning());
27777         config.resizeChild.clearPositioning();
27778         if(!config.width || !config.height){
27779             var csize = config.resizeChild.getSize();
27780             this.el.setSize(csize.width, csize.height);
27781         }
27782         if(config.pinned && !config.adjustments){
27783             config.adjustments = "auto";
27784         }
27785     }
27786
27787     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27788     this.proxy.unselectable();
27789     this.proxy.enableDisplayMode('block');
27790
27791     Roo.apply(this, config);
27792
27793     if(this.pinned){
27794         this.disableTrackOver = true;
27795         this.el.addClass("x-resizable-pinned");
27796     }
27797     // if the element isn't positioned, make it relative
27798     var position = this.el.getStyle("position");
27799     if(position != "absolute" && position != "fixed"){
27800         this.el.setStyle("position", "relative");
27801     }
27802     if(!this.handles){ // no handles passed, must be legacy style
27803         this.handles = 's,e,se';
27804         if(this.multiDirectional){
27805             this.handles += ',n,w';
27806         }
27807     }
27808     if(this.handles == "all"){
27809         this.handles = "n s e w ne nw se sw";
27810     }
27811     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27812     var ps = Roo.Resizable.positions;
27813     for(var i = 0, len = hs.length; i < len; i++){
27814         if(hs[i] && ps[hs[i]]){
27815             var pos = ps[hs[i]];
27816             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27817         }
27818     }
27819     // legacy
27820     this.corner = this.southeast;
27821     
27822     // updateBox = the box can move..
27823     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27824         this.updateBox = true;
27825     }
27826
27827     this.activeHandle = null;
27828
27829     if(this.resizeChild){
27830         if(typeof this.resizeChild == "boolean"){
27831             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27832         }else{
27833             this.resizeChild = Roo.get(this.resizeChild, true);
27834         }
27835     }
27836     
27837     if(this.adjustments == "auto"){
27838         var rc = this.resizeChild;
27839         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27840         if(rc && (hw || hn)){
27841             rc.position("relative");
27842             rc.setLeft(hw ? hw.el.getWidth() : 0);
27843             rc.setTop(hn ? hn.el.getHeight() : 0);
27844         }
27845         this.adjustments = [
27846             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27847             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27848         ];
27849     }
27850
27851     if(this.draggable){
27852         this.dd = this.dynamic ?
27853             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27854         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27855     }
27856
27857     // public events
27858     this.addEvents({
27859         /**
27860          * @event beforeresize
27861          * Fired before resize is allowed. Set enabled to false to cancel resize.
27862          * @param {Roo.Resizable} this
27863          * @param {Roo.EventObject} e The mousedown event
27864          */
27865         "beforeresize" : true,
27866         /**
27867          * @event resize
27868          * Fired after a resize.
27869          * @param {Roo.Resizable} this
27870          * @param {Number} width The new width
27871          * @param {Number} height The new height
27872          * @param {Roo.EventObject} e The mouseup event
27873          */
27874         "resize" : true
27875     });
27876
27877     if(this.width !== null && this.height !== null){
27878         this.resizeTo(this.width, this.height);
27879     }else{
27880         this.updateChildSize();
27881     }
27882     if(Roo.isIE){
27883         this.el.dom.style.zoom = 1;
27884     }
27885     Roo.Resizable.superclass.constructor.call(this);
27886 };
27887
27888 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27889         resizeChild : false,
27890         adjustments : [0, 0],
27891         minWidth : 5,
27892         minHeight : 5,
27893         maxWidth : 10000,
27894         maxHeight : 10000,
27895         enabled : true,
27896         animate : false,
27897         duration : .35,
27898         dynamic : false,
27899         handles : false,
27900         multiDirectional : false,
27901         disableTrackOver : false,
27902         easing : 'easeOutStrong',
27903         widthIncrement : 0,
27904         heightIncrement : 0,
27905         pinned : false,
27906         width : null,
27907         height : null,
27908         preserveRatio : false,
27909         transparent: false,
27910         minX: 0,
27911         minY: 0,
27912         draggable: false,
27913
27914         /**
27915          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27916          */
27917         constrainTo: undefined,
27918         /**
27919          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27920          */
27921         resizeRegion: undefined,
27922
27923
27924     /**
27925      * Perform a manual resize
27926      * @param {Number} width
27927      * @param {Number} height
27928      */
27929     resizeTo : function(width, height){
27930         this.el.setSize(width, height);
27931         this.updateChildSize();
27932         this.fireEvent("resize", this, width, height, null);
27933     },
27934
27935     // private
27936     startSizing : function(e, handle){
27937         this.fireEvent("beforeresize", this, e);
27938         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27939
27940             if(!this.overlay){
27941                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27942                 this.overlay.unselectable();
27943                 this.overlay.enableDisplayMode("block");
27944                 this.overlay.on("mousemove", this.onMouseMove, this);
27945                 this.overlay.on("mouseup", this.onMouseUp, this);
27946             }
27947             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27948
27949             this.resizing = true;
27950             this.startBox = this.el.getBox();
27951             this.startPoint = e.getXY();
27952             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27953                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27954
27955             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27956             this.overlay.show();
27957
27958             if(this.constrainTo) {
27959                 var ct = Roo.get(this.constrainTo);
27960                 this.resizeRegion = ct.getRegion().adjust(
27961                     ct.getFrameWidth('t'),
27962                     ct.getFrameWidth('l'),
27963                     -ct.getFrameWidth('b'),
27964                     -ct.getFrameWidth('r')
27965                 );
27966             }
27967
27968             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27969             this.proxy.show();
27970             this.proxy.setBox(this.startBox);
27971             if(!this.dynamic){
27972                 this.proxy.setStyle('visibility', 'visible');
27973             }
27974         }
27975     },
27976
27977     // private
27978     onMouseDown : function(handle, e){
27979         if(this.enabled){
27980             e.stopEvent();
27981             this.activeHandle = handle;
27982             this.startSizing(e, handle);
27983         }
27984     },
27985
27986     // private
27987     onMouseUp : function(e){
27988         var size = this.resizeElement();
27989         this.resizing = false;
27990         this.handleOut();
27991         this.overlay.hide();
27992         this.proxy.hide();
27993         this.fireEvent("resize", this, size.width, size.height, e);
27994     },
27995
27996     // private
27997     updateChildSize : function(){
27998         if(this.resizeChild){
27999             var el = this.el;
28000             var child = this.resizeChild;
28001             var adj = this.adjustments;
28002             if(el.dom.offsetWidth){
28003                 var b = el.getSize(true);
28004                 child.setSize(b.width+adj[0], b.height+adj[1]);
28005             }
28006             // Second call here for IE
28007             // The first call enables instant resizing and
28008             // the second call corrects scroll bars if they
28009             // exist
28010             if(Roo.isIE){
28011                 setTimeout(function(){
28012                     if(el.dom.offsetWidth){
28013                         var b = el.getSize(true);
28014                         child.setSize(b.width+adj[0], b.height+adj[1]);
28015                     }
28016                 }, 10);
28017             }
28018         }
28019     },
28020
28021     // private
28022     snap : function(value, inc, min){
28023         if(!inc || !value) return value;
28024         var newValue = value;
28025         var m = value % inc;
28026         if(m > 0){
28027             if(m > (inc/2)){
28028                 newValue = value + (inc-m);
28029             }else{
28030                 newValue = value - m;
28031             }
28032         }
28033         return Math.max(min, newValue);
28034     },
28035
28036     // private
28037     resizeElement : function(){
28038         var box = this.proxy.getBox();
28039         if(this.updateBox){
28040             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28041         }else{
28042             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28043         }
28044         this.updateChildSize();
28045         if(!this.dynamic){
28046             this.proxy.hide();
28047         }
28048         return box;
28049     },
28050
28051     // private
28052     constrain : function(v, diff, m, mx){
28053         if(v - diff < m){
28054             diff = v - m;
28055         }else if(v - diff > mx){
28056             diff = mx - v;
28057         }
28058         return diff;
28059     },
28060
28061     // private
28062     onMouseMove : function(e){
28063         if(this.enabled){
28064             try{// try catch so if something goes wrong the user doesn't get hung
28065
28066             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28067                 return;
28068             }
28069
28070             //var curXY = this.startPoint;
28071             var curSize = this.curSize || this.startBox;
28072             var x = this.startBox.x, y = this.startBox.y;
28073             var ox = x, oy = y;
28074             var w = curSize.width, h = curSize.height;
28075             var ow = w, oh = h;
28076             var mw = this.minWidth, mh = this.minHeight;
28077             var mxw = this.maxWidth, mxh = this.maxHeight;
28078             var wi = this.widthIncrement;
28079             var hi = this.heightIncrement;
28080
28081             var eventXY = e.getXY();
28082             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28083             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28084
28085             var pos = this.activeHandle.position;
28086
28087             switch(pos){
28088                 case "east":
28089                     w += diffX;
28090                     w = Math.min(Math.max(mw, w), mxw);
28091                     break;
28092              
28093                 case "south":
28094                     h += diffY;
28095                     h = Math.min(Math.max(mh, h), mxh);
28096                     break;
28097                 case "southeast":
28098                     w += diffX;
28099                     h += diffY;
28100                     w = Math.min(Math.max(mw, w), mxw);
28101                     h = Math.min(Math.max(mh, h), mxh);
28102                     break;
28103                 case "north":
28104                     diffY = this.constrain(h, diffY, mh, mxh);
28105                     y += diffY;
28106                     h -= diffY;
28107                     break;
28108                 case "hdrag":
28109                     
28110                     if (wi) {
28111                         var adiffX = Math.abs(diffX);
28112                         var sub = (adiffX % wi); // how much 
28113                         if (sub > (wi/2)) { // far enough to snap
28114                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28115                         } else {
28116                             // remove difference.. 
28117                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28118                         }
28119                     }
28120                     x += diffX;
28121                     x = Math.max(this.minX, x);
28122                     break;
28123                 case "west":
28124                     diffX = this.constrain(w, diffX, mw, mxw);
28125                     x += diffX;
28126                     w -= diffX;
28127                     break;
28128                 case "northeast":
28129                     w += diffX;
28130                     w = Math.min(Math.max(mw, w), mxw);
28131                     diffY = this.constrain(h, diffY, mh, mxh);
28132                     y += diffY;
28133                     h -= diffY;
28134                     break;
28135                 case "northwest":
28136                     diffX = this.constrain(w, diffX, mw, mxw);
28137                     diffY = this.constrain(h, diffY, mh, mxh);
28138                     y += diffY;
28139                     h -= diffY;
28140                     x += diffX;
28141                     w -= diffX;
28142                     break;
28143                case "southwest":
28144                     diffX = this.constrain(w, diffX, mw, mxw);
28145                     h += diffY;
28146                     h = Math.min(Math.max(mh, h), mxh);
28147                     x += diffX;
28148                     w -= diffX;
28149                     break;
28150             }
28151
28152             var sw = this.snap(w, wi, mw);
28153             var sh = this.snap(h, hi, mh);
28154             if(sw != w || sh != h){
28155                 switch(pos){
28156                     case "northeast":
28157                         y -= sh - h;
28158                     break;
28159                     case "north":
28160                         y -= sh - h;
28161                         break;
28162                     case "southwest":
28163                         x -= sw - w;
28164                     break;
28165                     case "west":
28166                         x -= sw - w;
28167                         break;
28168                     case "northwest":
28169                         x -= sw - w;
28170                         y -= sh - h;
28171                     break;
28172                 }
28173                 w = sw;
28174                 h = sh;
28175             }
28176
28177             if(this.preserveRatio){
28178                 switch(pos){
28179                     case "southeast":
28180                     case "east":
28181                         h = oh * (w/ow);
28182                         h = Math.min(Math.max(mh, h), mxh);
28183                         w = ow * (h/oh);
28184                        break;
28185                     case "south":
28186                         w = ow * (h/oh);
28187                         w = Math.min(Math.max(mw, w), mxw);
28188                         h = oh * (w/ow);
28189                         break;
28190                     case "northeast":
28191                         w = ow * (h/oh);
28192                         w = Math.min(Math.max(mw, w), mxw);
28193                         h = oh * (w/ow);
28194                     break;
28195                     case "north":
28196                         var tw = w;
28197                         w = ow * (h/oh);
28198                         w = Math.min(Math.max(mw, w), mxw);
28199                         h = oh * (w/ow);
28200                         x += (tw - w) / 2;
28201                         break;
28202                     case "southwest":
28203                         h = oh * (w/ow);
28204                         h = Math.min(Math.max(mh, h), mxh);
28205                         var tw = w;
28206                         w = ow * (h/oh);
28207                         x += tw - w;
28208                         break;
28209                     case "west":
28210                         var th = h;
28211                         h = oh * (w/ow);
28212                         h = Math.min(Math.max(mh, h), mxh);
28213                         y += (th - h) / 2;
28214                         var tw = w;
28215                         w = ow * (h/oh);
28216                         x += tw - w;
28217                        break;
28218                     case "northwest":
28219                         var tw = w;
28220                         var th = h;
28221                         h = oh * (w/ow);
28222                         h = Math.min(Math.max(mh, h), mxh);
28223                         w = ow * (h/oh);
28224                         y += th - h;
28225                         x += tw - w;
28226                        break;
28227
28228                 }
28229             }
28230             if (pos == 'hdrag') {
28231                 w = ow;
28232             }
28233             this.proxy.setBounds(x, y, w, h);
28234             if(this.dynamic){
28235                 this.resizeElement();
28236             }
28237             }catch(e){}
28238         }
28239     },
28240
28241     // private
28242     handleOver : function(){
28243         if(this.enabled){
28244             this.el.addClass("x-resizable-over");
28245         }
28246     },
28247
28248     // private
28249     handleOut : function(){
28250         if(!this.resizing){
28251             this.el.removeClass("x-resizable-over");
28252         }
28253     },
28254
28255     /**
28256      * Returns the element this component is bound to.
28257      * @return {Roo.Element}
28258      */
28259     getEl : function(){
28260         return this.el;
28261     },
28262
28263     /**
28264      * Returns the resizeChild element (or null).
28265      * @return {Roo.Element}
28266      */
28267     getResizeChild : function(){
28268         return this.resizeChild;
28269     },
28270
28271     /**
28272      * Destroys this resizable. If the element was wrapped and
28273      * removeEl is not true then the element remains.
28274      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28275      */
28276     destroy : function(removeEl){
28277         this.proxy.remove();
28278         if(this.overlay){
28279             this.overlay.removeAllListeners();
28280             this.overlay.remove();
28281         }
28282         var ps = Roo.Resizable.positions;
28283         for(var k in ps){
28284             if(typeof ps[k] != "function" && this[ps[k]]){
28285                 var h = this[ps[k]];
28286                 h.el.removeAllListeners();
28287                 h.el.remove();
28288             }
28289         }
28290         if(removeEl){
28291             this.el.update("");
28292             this.el.remove();
28293         }
28294     }
28295 });
28296
28297 // private
28298 // hash to map config positions to true positions
28299 Roo.Resizable.positions = {
28300     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28301     hd: "hdrag"
28302 };
28303
28304 // private
28305 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28306     if(!this.tpl){
28307         // only initialize the template if resizable is used
28308         var tpl = Roo.DomHelper.createTemplate(
28309             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28310         );
28311         tpl.compile();
28312         Roo.Resizable.Handle.prototype.tpl = tpl;
28313     }
28314     this.position = pos;
28315     this.rz = rz;
28316     // show north drag fro topdra
28317     var handlepos = pos == 'hdrag' ? 'north' : pos;
28318     
28319     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28320     if (pos == 'hdrag') {
28321         this.el.setStyle('cursor', 'pointer');
28322     }
28323     this.el.unselectable();
28324     if(transparent){
28325         this.el.setOpacity(0);
28326     }
28327     this.el.on("mousedown", this.onMouseDown, this);
28328     if(!disableTrackOver){
28329         this.el.on("mouseover", this.onMouseOver, this);
28330         this.el.on("mouseout", this.onMouseOut, this);
28331     }
28332 };
28333
28334 // private
28335 Roo.Resizable.Handle.prototype = {
28336     afterResize : function(rz){
28337         // do nothing
28338     },
28339     // private
28340     onMouseDown : function(e){
28341         this.rz.onMouseDown(this, e);
28342     },
28343     // private
28344     onMouseOver : function(e){
28345         this.rz.handleOver(this, e);
28346     },
28347     // private
28348     onMouseOut : function(e){
28349         this.rz.handleOut(this, e);
28350     }
28351 };/*
28352  * Based on:
28353  * Ext JS Library 1.1.1
28354  * Copyright(c) 2006-2007, Ext JS, LLC.
28355  *
28356  * Originally Released Under LGPL - original licence link has changed is not relivant.
28357  *
28358  * Fork - LGPL
28359  * <script type="text/javascript">
28360  */
28361
28362 /**
28363  * @class Roo.Editor
28364  * @extends Roo.Component
28365  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28366  * @constructor
28367  * Create a new Editor
28368  * @param {Roo.form.Field} field The Field object (or descendant)
28369  * @param {Object} config The config object
28370  */
28371 Roo.Editor = function(field, config){
28372     Roo.Editor.superclass.constructor.call(this, config);
28373     this.field = field;
28374     this.addEvents({
28375         /**
28376              * @event beforestartedit
28377              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28378              * false from the handler of this event.
28379              * @param {Editor} this
28380              * @param {Roo.Element} boundEl The underlying element bound to this editor
28381              * @param {Mixed} value The field value being set
28382              */
28383         "beforestartedit" : true,
28384         /**
28385              * @event startedit
28386              * Fires when this editor is displayed
28387              * @param {Roo.Element} boundEl The underlying element bound to this editor
28388              * @param {Mixed} value The starting field value
28389              */
28390         "startedit" : true,
28391         /**
28392              * @event beforecomplete
28393              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28394              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28395              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28396              * event will not fire since no edit actually occurred.
28397              * @param {Editor} this
28398              * @param {Mixed} value The current field value
28399              * @param {Mixed} startValue The original field value
28400              */
28401         "beforecomplete" : true,
28402         /**
28403              * @event complete
28404              * Fires after editing is complete and any changed value has been written to the underlying field.
28405              * @param {Editor} this
28406              * @param {Mixed} value The current field value
28407              * @param {Mixed} startValue The original field value
28408              */
28409         "complete" : true,
28410         /**
28411          * @event specialkey
28412          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28413          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28414          * @param {Roo.form.Field} this
28415          * @param {Roo.EventObject} e The event object
28416          */
28417         "specialkey" : true
28418     });
28419 };
28420
28421 Roo.extend(Roo.Editor, Roo.Component, {
28422     /**
28423      * @cfg {Boolean/String} autosize
28424      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28425      * or "height" to adopt the height only (defaults to false)
28426      */
28427     /**
28428      * @cfg {Boolean} revertInvalid
28429      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28430      * validation fails (defaults to true)
28431      */
28432     /**
28433      * @cfg {Boolean} ignoreNoChange
28434      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28435      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28436      * will never be ignored.
28437      */
28438     /**
28439      * @cfg {Boolean} hideEl
28440      * False to keep the bound element visible while the editor is displayed (defaults to true)
28441      */
28442     /**
28443      * @cfg {Mixed} value
28444      * The data value of the underlying field (defaults to "")
28445      */
28446     value : "",
28447     /**
28448      * @cfg {String} alignment
28449      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28450      */
28451     alignment: "c-c?",
28452     /**
28453      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28454      * for bottom-right shadow (defaults to "frame")
28455      */
28456     shadow : "frame",
28457     /**
28458      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28459      */
28460     constrain : false,
28461     /**
28462      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28463      */
28464     completeOnEnter : false,
28465     /**
28466      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28467      */
28468     cancelOnEsc : false,
28469     /**
28470      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28471      */
28472     updateEl : false,
28473
28474     // private
28475     onRender : function(ct, position){
28476         this.el = new Roo.Layer({
28477             shadow: this.shadow,
28478             cls: "x-editor",
28479             parentEl : ct,
28480             shim : this.shim,
28481             shadowOffset:4,
28482             id: this.id,
28483             constrain: this.constrain
28484         });
28485         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28486         if(this.field.msgTarget != 'title'){
28487             this.field.msgTarget = 'qtip';
28488         }
28489         this.field.render(this.el);
28490         if(Roo.isGecko){
28491             this.field.el.dom.setAttribute('autocomplete', 'off');
28492         }
28493         this.field.on("specialkey", this.onSpecialKey, this);
28494         if(this.swallowKeys){
28495             this.field.el.swallowEvent(['keydown','keypress']);
28496         }
28497         this.field.show();
28498         this.field.on("blur", this.onBlur, this);
28499         if(this.field.grow){
28500             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28501         }
28502     },
28503
28504     onSpecialKey : function(field, e)
28505     {
28506         //Roo.log('editor onSpecialKey');
28507         if(this.completeOnEnter && e.getKey() == e.ENTER){
28508             e.stopEvent();
28509             this.completeEdit();
28510             return;
28511         }
28512         // do not fire special key otherwise it might hide close the editor...
28513         if(e.getKey() == e.ENTER){    
28514             return;
28515         }
28516         if(this.cancelOnEsc && e.getKey() == e.ESC){
28517             this.cancelEdit();
28518             return;
28519         } 
28520         this.fireEvent('specialkey', field, e);
28521     
28522     },
28523
28524     /**
28525      * Starts the editing process and shows the editor.
28526      * @param {String/HTMLElement/Element} el The element to edit
28527      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28528       * to the innerHTML of el.
28529      */
28530     startEdit : function(el, value){
28531         if(this.editing){
28532             this.completeEdit();
28533         }
28534         this.boundEl = Roo.get(el);
28535         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28536         if(!this.rendered){
28537             this.render(this.parentEl || document.body);
28538         }
28539         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28540             return;
28541         }
28542         this.startValue = v;
28543         this.field.setValue(v);
28544         if(this.autoSize){
28545             var sz = this.boundEl.getSize();
28546             switch(this.autoSize){
28547                 case "width":
28548                 this.setSize(sz.width,  "");
28549                 break;
28550                 case "height":
28551                 this.setSize("",  sz.height);
28552                 break;
28553                 default:
28554                 this.setSize(sz.width,  sz.height);
28555             }
28556         }
28557         this.el.alignTo(this.boundEl, this.alignment);
28558         this.editing = true;
28559         if(Roo.QuickTips){
28560             Roo.QuickTips.disable();
28561         }
28562         this.show();
28563     },
28564
28565     /**
28566      * Sets the height and width of this editor.
28567      * @param {Number} width The new width
28568      * @param {Number} height The new height
28569      */
28570     setSize : function(w, h){
28571         this.field.setSize(w, h);
28572         if(this.el){
28573             this.el.sync();
28574         }
28575     },
28576
28577     /**
28578      * Realigns the editor to the bound field based on the current alignment config value.
28579      */
28580     realign : function(){
28581         this.el.alignTo(this.boundEl, this.alignment);
28582     },
28583
28584     /**
28585      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28586      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28587      */
28588     completeEdit : function(remainVisible){
28589         if(!this.editing){
28590             return;
28591         }
28592         var v = this.getValue();
28593         if(this.revertInvalid !== false && !this.field.isValid()){
28594             v = this.startValue;
28595             this.cancelEdit(true);
28596         }
28597         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28598             this.editing = false;
28599             this.hide();
28600             return;
28601         }
28602         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28603             this.editing = false;
28604             if(this.updateEl && this.boundEl){
28605                 this.boundEl.update(v);
28606             }
28607             if(remainVisible !== true){
28608                 this.hide();
28609             }
28610             this.fireEvent("complete", this, v, this.startValue);
28611         }
28612     },
28613
28614     // private
28615     onShow : function(){
28616         this.el.show();
28617         if(this.hideEl !== false){
28618             this.boundEl.hide();
28619         }
28620         this.field.show();
28621         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28622             this.fixIEFocus = true;
28623             this.deferredFocus.defer(50, this);
28624         }else{
28625             this.field.focus();
28626         }
28627         this.fireEvent("startedit", this.boundEl, this.startValue);
28628     },
28629
28630     deferredFocus : function(){
28631         if(this.editing){
28632             this.field.focus();
28633         }
28634     },
28635
28636     /**
28637      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28638      * reverted to the original starting value.
28639      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28640      * cancel (defaults to false)
28641      */
28642     cancelEdit : function(remainVisible){
28643         if(this.editing){
28644             this.setValue(this.startValue);
28645             if(remainVisible !== true){
28646                 this.hide();
28647             }
28648         }
28649     },
28650
28651     // private
28652     onBlur : function(){
28653         if(this.allowBlur !== true && this.editing){
28654             this.completeEdit();
28655         }
28656     },
28657
28658     // private
28659     onHide : function(){
28660         if(this.editing){
28661             this.completeEdit();
28662             return;
28663         }
28664         this.field.blur();
28665         if(this.field.collapse){
28666             this.field.collapse();
28667         }
28668         this.el.hide();
28669         if(this.hideEl !== false){
28670             this.boundEl.show();
28671         }
28672         if(Roo.QuickTips){
28673             Roo.QuickTips.enable();
28674         }
28675     },
28676
28677     /**
28678      * Sets the data value of the editor
28679      * @param {Mixed} value Any valid value supported by the underlying field
28680      */
28681     setValue : function(v){
28682         this.field.setValue(v);
28683     },
28684
28685     /**
28686      * Gets the data value of the editor
28687      * @return {Mixed} The data value
28688      */
28689     getValue : function(){
28690         return this.field.getValue();
28691     }
28692 });/*
28693  * Based on:
28694  * Ext JS Library 1.1.1
28695  * Copyright(c) 2006-2007, Ext JS, LLC.
28696  *
28697  * Originally Released Under LGPL - original licence link has changed is not relivant.
28698  *
28699  * Fork - LGPL
28700  * <script type="text/javascript">
28701  */
28702  
28703 /**
28704  * @class Roo.BasicDialog
28705  * @extends Roo.util.Observable
28706  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28707  * <pre><code>
28708 var dlg = new Roo.BasicDialog("my-dlg", {
28709     height: 200,
28710     width: 300,
28711     minHeight: 100,
28712     minWidth: 150,
28713     modal: true,
28714     proxyDrag: true,
28715     shadow: true
28716 });
28717 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28718 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28719 dlg.addButton('Cancel', dlg.hide, dlg);
28720 dlg.show();
28721 </code></pre>
28722   <b>A Dialog should always be a direct child of the body element.</b>
28723  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28724  * @cfg {String} title Default text to display in the title bar (defaults to null)
28725  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28726  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28727  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28728  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28729  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28730  * (defaults to null with no animation)
28731  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28732  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28733  * property for valid values (defaults to 'all')
28734  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28735  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28736  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28737  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28738  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28739  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28740  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28741  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28742  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28743  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28744  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28745  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28746  * draggable = true (defaults to false)
28747  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28748  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28749  * shadow (defaults to false)
28750  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28751  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28752  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28753  * @cfg {Array} buttons Array of buttons
28754  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28755  * @constructor
28756  * Create a new BasicDialog.
28757  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28758  * @param {Object} config Configuration options
28759  */
28760 Roo.BasicDialog = function(el, config){
28761     this.el = Roo.get(el);
28762     var dh = Roo.DomHelper;
28763     if(!this.el && config && config.autoCreate){
28764         if(typeof config.autoCreate == "object"){
28765             if(!config.autoCreate.id){
28766                 config.autoCreate.id = el;
28767             }
28768             this.el = dh.append(document.body,
28769                         config.autoCreate, true);
28770         }else{
28771             this.el = dh.append(document.body,
28772                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28773         }
28774     }
28775     el = this.el;
28776     el.setDisplayed(true);
28777     el.hide = this.hideAction;
28778     this.id = el.id;
28779     el.addClass("x-dlg");
28780
28781     Roo.apply(this, config);
28782
28783     this.proxy = el.createProxy("x-dlg-proxy");
28784     this.proxy.hide = this.hideAction;
28785     this.proxy.setOpacity(.5);
28786     this.proxy.hide();
28787
28788     if(config.width){
28789         el.setWidth(config.width);
28790     }
28791     if(config.height){
28792         el.setHeight(config.height);
28793     }
28794     this.size = el.getSize();
28795     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28796         this.xy = [config.x,config.y];
28797     }else{
28798         this.xy = el.getCenterXY(true);
28799     }
28800     /** The header element @type Roo.Element */
28801     this.header = el.child("> .x-dlg-hd");
28802     /** The body element @type Roo.Element */
28803     this.body = el.child("> .x-dlg-bd");
28804     /** The footer element @type Roo.Element */
28805     this.footer = el.child("> .x-dlg-ft");
28806
28807     if(!this.header){
28808         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28809     }
28810     if(!this.body){
28811         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28812     }
28813
28814     this.header.unselectable();
28815     if(this.title){
28816         this.header.update(this.title);
28817     }
28818     // this element allows the dialog to be focused for keyboard event
28819     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28820     this.focusEl.swallowEvent("click", true);
28821
28822     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28823
28824     // wrap the body and footer for special rendering
28825     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28826     if(this.footer){
28827         this.bwrap.dom.appendChild(this.footer.dom);
28828     }
28829
28830     this.bg = this.el.createChild({
28831         tag: "div", cls:"x-dlg-bg",
28832         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28833     });
28834     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28835
28836
28837     if(this.autoScroll !== false && !this.autoTabs){
28838         this.body.setStyle("overflow", "auto");
28839     }
28840
28841     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28842
28843     if(this.closable !== false){
28844         this.el.addClass("x-dlg-closable");
28845         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28846         this.close.on("click", this.closeClick, this);
28847         this.close.addClassOnOver("x-dlg-close-over");
28848     }
28849     if(this.collapsible !== false){
28850         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28851         this.collapseBtn.on("click", this.collapseClick, this);
28852         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28853         this.header.on("dblclick", this.collapseClick, this);
28854     }
28855     if(this.resizable !== false){
28856         this.el.addClass("x-dlg-resizable");
28857         this.resizer = new Roo.Resizable(el, {
28858             minWidth: this.minWidth || 80,
28859             minHeight:this.minHeight || 80,
28860             handles: this.resizeHandles || "all",
28861             pinned: true
28862         });
28863         this.resizer.on("beforeresize", this.beforeResize, this);
28864         this.resizer.on("resize", this.onResize, this);
28865     }
28866     if(this.draggable !== false){
28867         el.addClass("x-dlg-draggable");
28868         if (!this.proxyDrag) {
28869             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28870         }
28871         else {
28872             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28873         }
28874         dd.setHandleElId(this.header.id);
28875         dd.endDrag = this.endMove.createDelegate(this);
28876         dd.startDrag = this.startMove.createDelegate(this);
28877         dd.onDrag = this.onDrag.createDelegate(this);
28878         dd.scroll = false;
28879         this.dd = dd;
28880     }
28881     if(this.modal){
28882         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28883         this.mask.enableDisplayMode("block");
28884         this.mask.hide();
28885         this.el.addClass("x-dlg-modal");
28886     }
28887     if(this.shadow){
28888         this.shadow = new Roo.Shadow({
28889             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28890             offset : this.shadowOffset
28891         });
28892     }else{
28893         this.shadowOffset = 0;
28894     }
28895     if(Roo.useShims && this.shim !== false){
28896         this.shim = this.el.createShim();
28897         this.shim.hide = this.hideAction;
28898         this.shim.hide();
28899     }else{
28900         this.shim = false;
28901     }
28902     if(this.autoTabs){
28903         this.initTabs();
28904     }
28905     if (this.buttons) { 
28906         var bts= this.buttons;
28907         this.buttons = [];
28908         Roo.each(bts, function(b) {
28909             this.addButton(b);
28910         }, this);
28911     }
28912     
28913     
28914     this.addEvents({
28915         /**
28916          * @event keydown
28917          * Fires when a key is pressed
28918          * @param {Roo.BasicDialog} this
28919          * @param {Roo.EventObject} e
28920          */
28921         "keydown" : true,
28922         /**
28923          * @event move
28924          * Fires when this dialog is moved by the user.
28925          * @param {Roo.BasicDialog} this
28926          * @param {Number} x The new page X
28927          * @param {Number} y The new page Y
28928          */
28929         "move" : true,
28930         /**
28931          * @event resize
28932          * Fires when this dialog is resized by the user.
28933          * @param {Roo.BasicDialog} this
28934          * @param {Number} width The new width
28935          * @param {Number} height The new height
28936          */
28937         "resize" : true,
28938         /**
28939          * @event beforehide
28940          * Fires before this dialog is hidden.
28941          * @param {Roo.BasicDialog} this
28942          */
28943         "beforehide" : true,
28944         /**
28945          * @event hide
28946          * Fires when this dialog is hidden.
28947          * @param {Roo.BasicDialog} this
28948          */
28949         "hide" : true,
28950         /**
28951          * @event beforeshow
28952          * Fires before this dialog is shown.
28953          * @param {Roo.BasicDialog} this
28954          */
28955         "beforeshow" : true,
28956         /**
28957          * @event show
28958          * Fires when this dialog is shown.
28959          * @param {Roo.BasicDialog} this
28960          */
28961         "show" : true
28962     });
28963     el.on("keydown", this.onKeyDown, this);
28964     el.on("mousedown", this.toFront, this);
28965     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28966     this.el.hide();
28967     Roo.DialogManager.register(this);
28968     Roo.BasicDialog.superclass.constructor.call(this);
28969 };
28970
28971 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28972     shadowOffset: Roo.isIE ? 6 : 5,
28973     minHeight: 80,
28974     minWidth: 200,
28975     minButtonWidth: 75,
28976     defaultButton: null,
28977     buttonAlign: "right",
28978     tabTag: 'div',
28979     firstShow: true,
28980
28981     /**
28982      * Sets the dialog title text
28983      * @param {String} text The title text to display
28984      * @return {Roo.BasicDialog} this
28985      */
28986     setTitle : function(text){
28987         this.header.update(text);
28988         return this;
28989     },
28990
28991     // private
28992     closeClick : function(){
28993         this.hide();
28994     },
28995
28996     // private
28997     collapseClick : function(){
28998         this[this.collapsed ? "expand" : "collapse"]();
28999     },
29000
29001     /**
29002      * Collapses the dialog to its minimized state (only the title bar is visible).
29003      * Equivalent to the user clicking the collapse dialog button.
29004      */
29005     collapse : function(){
29006         if(!this.collapsed){
29007             this.collapsed = true;
29008             this.el.addClass("x-dlg-collapsed");
29009             this.restoreHeight = this.el.getHeight();
29010             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29011         }
29012     },
29013
29014     /**
29015      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29016      * clicking the expand dialog button.
29017      */
29018     expand : function(){
29019         if(this.collapsed){
29020             this.collapsed = false;
29021             this.el.removeClass("x-dlg-collapsed");
29022             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29023         }
29024     },
29025
29026     /**
29027      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29028      * @return {Roo.TabPanel} The tabs component
29029      */
29030     initTabs : function(){
29031         var tabs = this.getTabs();
29032         while(tabs.getTab(0)){
29033             tabs.removeTab(0);
29034         }
29035         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29036             var dom = el.dom;
29037             tabs.addTab(Roo.id(dom), dom.title);
29038             dom.title = "";
29039         });
29040         tabs.activate(0);
29041         return tabs;
29042     },
29043
29044     // private
29045     beforeResize : function(){
29046         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29047     },
29048
29049     // private
29050     onResize : function(){
29051         this.refreshSize();
29052         this.syncBodyHeight();
29053         this.adjustAssets();
29054         this.focus();
29055         this.fireEvent("resize", this, this.size.width, this.size.height);
29056     },
29057
29058     // private
29059     onKeyDown : function(e){
29060         if(this.isVisible()){
29061             this.fireEvent("keydown", this, e);
29062         }
29063     },
29064
29065     /**
29066      * Resizes the dialog.
29067      * @param {Number} width
29068      * @param {Number} height
29069      * @return {Roo.BasicDialog} this
29070      */
29071     resizeTo : function(width, height){
29072         this.el.setSize(width, height);
29073         this.size = {width: width, height: height};
29074         this.syncBodyHeight();
29075         if(this.fixedcenter){
29076             this.center();
29077         }
29078         if(this.isVisible()){
29079             this.constrainXY();
29080             this.adjustAssets();
29081         }
29082         this.fireEvent("resize", this, width, height);
29083         return this;
29084     },
29085
29086
29087     /**
29088      * Resizes the dialog to fit the specified content size.
29089      * @param {Number} width
29090      * @param {Number} height
29091      * @return {Roo.BasicDialog} this
29092      */
29093     setContentSize : function(w, h){
29094         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29095         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29096         //if(!this.el.isBorderBox()){
29097             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29098             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29099         //}
29100         if(this.tabs){
29101             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29102             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29103         }
29104         this.resizeTo(w, h);
29105         return this;
29106     },
29107
29108     /**
29109      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29110      * executed in response to a particular key being pressed while the dialog is active.
29111      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29112      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29113      * @param {Function} fn The function to call
29114      * @param {Object} scope (optional) The scope of the function
29115      * @return {Roo.BasicDialog} this
29116      */
29117     addKeyListener : function(key, fn, scope){
29118         var keyCode, shift, ctrl, alt;
29119         if(typeof key == "object" && !(key instanceof Array)){
29120             keyCode = key["key"];
29121             shift = key["shift"];
29122             ctrl = key["ctrl"];
29123             alt = key["alt"];
29124         }else{
29125             keyCode = key;
29126         }
29127         var handler = function(dlg, e){
29128             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29129                 var k = e.getKey();
29130                 if(keyCode instanceof Array){
29131                     for(var i = 0, len = keyCode.length; i < len; i++){
29132                         if(keyCode[i] == k){
29133                           fn.call(scope || window, dlg, k, e);
29134                           return;
29135                         }
29136                     }
29137                 }else{
29138                     if(k == keyCode){
29139                         fn.call(scope || window, dlg, k, e);
29140                     }
29141                 }
29142             }
29143         };
29144         this.on("keydown", handler);
29145         return this;
29146     },
29147
29148     /**
29149      * Returns the TabPanel component (creates it if it doesn't exist).
29150      * Note: If you wish to simply check for the existence of tabs without creating them,
29151      * check for a null 'tabs' property.
29152      * @return {Roo.TabPanel} The tabs component
29153      */
29154     getTabs : function(){
29155         if(!this.tabs){
29156             this.el.addClass("x-dlg-auto-tabs");
29157             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29158             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29159         }
29160         return this.tabs;
29161     },
29162
29163     /**
29164      * Adds a button to the footer section of the dialog.
29165      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29166      * object or a valid Roo.DomHelper element config
29167      * @param {Function} handler The function called when the button is clicked
29168      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29169      * @return {Roo.Button} The new button
29170      */
29171     addButton : function(config, handler, scope){
29172         var dh = Roo.DomHelper;
29173         if(!this.footer){
29174             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29175         }
29176         if(!this.btnContainer){
29177             var tb = this.footer.createChild({
29178
29179                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29180                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29181             }, null, true);
29182             this.btnContainer = tb.firstChild.firstChild.firstChild;
29183         }
29184         var bconfig = {
29185             handler: handler,
29186             scope: scope,
29187             minWidth: this.minButtonWidth,
29188             hideParent:true
29189         };
29190         if(typeof config == "string"){
29191             bconfig.text = config;
29192         }else{
29193             if(config.tag){
29194                 bconfig.dhconfig = config;
29195             }else{
29196                 Roo.apply(bconfig, config);
29197             }
29198         }
29199         var fc = false;
29200         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29201             bconfig.position = Math.max(0, bconfig.position);
29202             fc = this.btnContainer.childNodes[bconfig.position];
29203         }
29204          
29205         var btn = new Roo.Button(
29206             fc ? 
29207                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29208                 : this.btnContainer.appendChild(document.createElement("td")),
29209             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29210             bconfig
29211         );
29212         this.syncBodyHeight();
29213         if(!this.buttons){
29214             /**
29215              * Array of all the buttons that have been added to this dialog via addButton
29216              * @type Array
29217              */
29218             this.buttons = [];
29219         }
29220         this.buttons.push(btn);
29221         return btn;
29222     },
29223
29224     /**
29225      * Sets the default button to be focused when the dialog is displayed.
29226      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29227      * @return {Roo.BasicDialog} this
29228      */
29229     setDefaultButton : function(btn){
29230         this.defaultButton = btn;
29231         return this;
29232     },
29233
29234     // private
29235     getHeaderFooterHeight : function(safe){
29236         var height = 0;
29237         if(this.header){
29238            height += this.header.getHeight();
29239         }
29240         if(this.footer){
29241            var fm = this.footer.getMargins();
29242             height += (this.footer.getHeight()+fm.top+fm.bottom);
29243         }
29244         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29245         height += this.centerBg.getPadding("tb");
29246         return height;
29247     },
29248
29249     // private
29250     syncBodyHeight : function(){
29251         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29252         var height = this.size.height - this.getHeaderFooterHeight(false);
29253         bd.setHeight(height-bd.getMargins("tb"));
29254         var hh = this.header.getHeight();
29255         var h = this.size.height-hh;
29256         cb.setHeight(h);
29257         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29258         bw.setHeight(h-cb.getPadding("tb"));
29259         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29260         bd.setWidth(bw.getWidth(true));
29261         if(this.tabs){
29262             this.tabs.syncHeight();
29263             if(Roo.isIE){
29264                 this.tabs.el.repaint();
29265             }
29266         }
29267     },
29268
29269     /**
29270      * Restores the previous state of the dialog if Roo.state is configured.
29271      * @return {Roo.BasicDialog} this
29272      */
29273     restoreState : function(){
29274         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29275         if(box && box.width){
29276             this.xy = [box.x, box.y];
29277             this.resizeTo(box.width, box.height);
29278         }
29279         return this;
29280     },
29281
29282     // private
29283     beforeShow : function(){
29284         this.expand();
29285         if(this.fixedcenter){
29286             this.xy = this.el.getCenterXY(true);
29287         }
29288         if(this.modal){
29289             Roo.get(document.body).addClass("x-body-masked");
29290             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29291             this.mask.show();
29292         }
29293         this.constrainXY();
29294     },
29295
29296     // private
29297     animShow : function(){
29298         var b = Roo.get(this.animateTarget).getBox();
29299         this.proxy.setSize(b.width, b.height);
29300         this.proxy.setLocation(b.x, b.y);
29301         this.proxy.show();
29302         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29303                     true, .35, this.showEl.createDelegate(this));
29304     },
29305
29306     /**
29307      * Shows the dialog.
29308      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29309      * @return {Roo.BasicDialog} this
29310      */
29311     show : function(animateTarget){
29312         if (this.fireEvent("beforeshow", this) === false){
29313             return;
29314         }
29315         if(this.syncHeightBeforeShow){
29316             this.syncBodyHeight();
29317         }else if(this.firstShow){
29318             this.firstShow = false;
29319             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29320         }
29321         this.animateTarget = animateTarget || this.animateTarget;
29322         if(!this.el.isVisible()){
29323             this.beforeShow();
29324             if(this.animateTarget && Roo.get(this.animateTarget)){
29325                 this.animShow();
29326             }else{
29327                 this.showEl();
29328             }
29329         }
29330         return this;
29331     },
29332
29333     // private
29334     showEl : function(){
29335         this.proxy.hide();
29336         this.el.setXY(this.xy);
29337         this.el.show();
29338         this.adjustAssets(true);
29339         this.toFront();
29340         this.focus();
29341         // IE peekaboo bug - fix found by Dave Fenwick
29342         if(Roo.isIE){
29343             this.el.repaint();
29344         }
29345         this.fireEvent("show", this);
29346     },
29347
29348     /**
29349      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29350      * dialog itself will receive focus.
29351      */
29352     focus : function(){
29353         if(this.defaultButton){
29354             this.defaultButton.focus();
29355         }else{
29356             this.focusEl.focus();
29357         }
29358     },
29359
29360     // private
29361     constrainXY : function(){
29362         if(this.constraintoviewport !== false){
29363             if(!this.viewSize){
29364                 if(this.container){
29365                     var s = this.container.getSize();
29366                     this.viewSize = [s.width, s.height];
29367                 }else{
29368                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29369                 }
29370             }
29371             var s = Roo.get(this.container||document).getScroll();
29372
29373             var x = this.xy[0], y = this.xy[1];
29374             var w = this.size.width, h = this.size.height;
29375             var vw = this.viewSize[0], vh = this.viewSize[1];
29376             // only move it if it needs it
29377             var moved = false;
29378             // first validate right/bottom
29379             if(x + w > vw+s.left){
29380                 x = vw - w;
29381                 moved = true;
29382             }
29383             if(y + h > vh+s.top){
29384                 y = vh - h;
29385                 moved = true;
29386             }
29387             // then make sure top/left isn't negative
29388             if(x < s.left){
29389                 x = s.left;
29390                 moved = true;
29391             }
29392             if(y < s.top){
29393                 y = s.top;
29394                 moved = true;
29395             }
29396             if(moved){
29397                 // cache xy
29398                 this.xy = [x, y];
29399                 if(this.isVisible()){
29400                     this.el.setLocation(x, y);
29401                     this.adjustAssets();
29402                 }
29403             }
29404         }
29405     },
29406
29407     // private
29408     onDrag : function(){
29409         if(!this.proxyDrag){
29410             this.xy = this.el.getXY();
29411             this.adjustAssets();
29412         }
29413     },
29414
29415     // private
29416     adjustAssets : function(doShow){
29417         var x = this.xy[0], y = this.xy[1];
29418         var w = this.size.width, h = this.size.height;
29419         if(doShow === true){
29420             if(this.shadow){
29421                 this.shadow.show(this.el);
29422             }
29423             if(this.shim){
29424                 this.shim.show();
29425             }
29426         }
29427         if(this.shadow && this.shadow.isVisible()){
29428             this.shadow.show(this.el);
29429         }
29430         if(this.shim && this.shim.isVisible()){
29431             this.shim.setBounds(x, y, w, h);
29432         }
29433     },
29434
29435     // private
29436     adjustViewport : function(w, h){
29437         if(!w || !h){
29438             w = Roo.lib.Dom.getViewWidth();
29439             h = Roo.lib.Dom.getViewHeight();
29440         }
29441         // cache the size
29442         this.viewSize = [w, h];
29443         if(this.modal && this.mask.isVisible()){
29444             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29445             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29446         }
29447         if(this.isVisible()){
29448             this.constrainXY();
29449         }
29450     },
29451
29452     /**
29453      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29454      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29455      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29456      */
29457     destroy : function(removeEl){
29458         if(this.isVisible()){
29459             this.animateTarget = null;
29460             this.hide();
29461         }
29462         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29463         if(this.tabs){
29464             this.tabs.destroy(removeEl);
29465         }
29466         Roo.destroy(
29467              this.shim,
29468              this.proxy,
29469              this.resizer,
29470              this.close,
29471              this.mask
29472         );
29473         if(this.dd){
29474             this.dd.unreg();
29475         }
29476         if(this.buttons){
29477            for(var i = 0, len = this.buttons.length; i < len; i++){
29478                this.buttons[i].destroy();
29479            }
29480         }
29481         this.el.removeAllListeners();
29482         if(removeEl === true){
29483             this.el.update("");
29484             this.el.remove();
29485         }
29486         Roo.DialogManager.unregister(this);
29487     },
29488
29489     // private
29490     startMove : function(){
29491         if(this.proxyDrag){
29492             this.proxy.show();
29493         }
29494         if(this.constraintoviewport !== false){
29495             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29496         }
29497     },
29498
29499     // private
29500     endMove : function(){
29501         if(!this.proxyDrag){
29502             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29503         }else{
29504             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29505             this.proxy.hide();
29506         }
29507         this.refreshSize();
29508         this.adjustAssets();
29509         this.focus();
29510         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29511     },
29512
29513     /**
29514      * Brings this dialog to the front of any other visible dialogs
29515      * @return {Roo.BasicDialog} this
29516      */
29517     toFront : function(){
29518         Roo.DialogManager.bringToFront(this);
29519         return this;
29520     },
29521
29522     /**
29523      * Sends this dialog to the back (under) of any other visible dialogs
29524      * @return {Roo.BasicDialog} this
29525      */
29526     toBack : function(){
29527         Roo.DialogManager.sendToBack(this);
29528         return this;
29529     },
29530
29531     /**
29532      * Centers this dialog in the viewport
29533      * @return {Roo.BasicDialog} this
29534      */
29535     center : function(){
29536         var xy = this.el.getCenterXY(true);
29537         this.moveTo(xy[0], xy[1]);
29538         return this;
29539     },
29540
29541     /**
29542      * Moves the dialog's top-left corner to the specified point
29543      * @param {Number} x
29544      * @param {Number} y
29545      * @return {Roo.BasicDialog} this
29546      */
29547     moveTo : function(x, y){
29548         this.xy = [x,y];
29549         if(this.isVisible()){
29550             this.el.setXY(this.xy);
29551             this.adjustAssets();
29552         }
29553         return this;
29554     },
29555
29556     /**
29557      * Aligns the dialog to the specified element
29558      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29559      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29560      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29561      * @return {Roo.BasicDialog} this
29562      */
29563     alignTo : function(element, position, offsets){
29564         this.xy = this.el.getAlignToXY(element, position, offsets);
29565         if(this.isVisible()){
29566             this.el.setXY(this.xy);
29567             this.adjustAssets();
29568         }
29569         return this;
29570     },
29571
29572     /**
29573      * Anchors an element to another element and realigns it when the window is resized.
29574      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29575      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29576      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29577      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29578      * is a number, it is used as the buffer delay (defaults to 50ms).
29579      * @return {Roo.BasicDialog} this
29580      */
29581     anchorTo : function(el, alignment, offsets, monitorScroll){
29582         var action = function(){
29583             this.alignTo(el, alignment, offsets);
29584         };
29585         Roo.EventManager.onWindowResize(action, this);
29586         var tm = typeof monitorScroll;
29587         if(tm != 'undefined'){
29588             Roo.EventManager.on(window, 'scroll', action, this,
29589                 {buffer: tm == 'number' ? monitorScroll : 50});
29590         }
29591         action.call(this);
29592         return this;
29593     },
29594
29595     /**
29596      * Returns true if the dialog is visible
29597      * @return {Boolean}
29598      */
29599     isVisible : function(){
29600         return this.el.isVisible();
29601     },
29602
29603     // private
29604     animHide : function(callback){
29605         var b = Roo.get(this.animateTarget).getBox();
29606         this.proxy.show();
29607         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29608         this.el.hide();
29609         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29610                     this.hideEl.createDelegate(this, [callback]));
29611     },
29612
29613     /**
29614      * Hides the dialog.
29615      * @param {Function} callback (optional) Function to call when the dialog is hidden
29616      * @return {Roo.BasicDialog} this
29617      */
29618     hide : function(callback){
29619         if (this.fireEvent("beforehide", this) === false){
29620             return;
29621         }
29622         if(this.shadow){
29623             this.shadow.hide();
29624         }
29625         if(this.shim) {
29626           this.shim.hide();
29627         }
29628         // sometimes animateTarget seems to get set.. causing problems...
29629         // this just double checks..
29630         if(this.animateTarget && Roo.get(this.animateTarget)) {
29631            this.animHide(callback);
29632         }else{
29633             this.el.hide();
29634             this.hideEl(callback);
29635         }
29636         return this;
29637     },
29638
29639     // private
29640     hideEl : function(callback){
29641         this.proxy.hide();
29642         if(this.modal){
29643             this.mask.hide();
29644             Roo.get(document.body).removeClass("x-body-masked");
29645         }
29646         this.fireEvent("hide", this);
29647         if(typeof callback == "function"){
29648             callback();
29649         }
29650     },
29651
29652     // private
29653     hideAction : function(){
29654         this.setLeft("-10000px");
29655         this.setTop("-10000px");
29656         this.setStyle("visibility", "hidden");
29657     },
29658
29659     // private
29660     refreshSize : function(){
29661         this.size = this.el.getSize();
29662         this.xy = this.el.getXY();
29663         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29664     },
29665
29666     // private
29667     // z-index is managed by the DialogManager and may be overwritten at any time
29668     setZIndex : function(index){
29669         if(this.modal){
29670             this.mask.setStyle("z-index", index);
29671         }
29672         if(this.shim){
29673             this.shim.setStyle("z-index", ++index);
29674         }
29675         if(this.shadow){
29676             this.shadow.setZIndex(++index);
29677         }
29678         this.el.setStyle("z-index", ++index);
29679         if(this.proxy){
29680             this.proxy.setStyle("z-index", ++index);
29681         }
29682         if(this.resizer){
29683             this.resizer.proxy.setStyle("z-index", ++index);
29684         }
29685
29686         this.lastZIndex = index;
29687     },
29688
29689     /**
29690      * Returns the element for this dialog
29691      * @return {Roo.Element} The underlying dialog Element
29692      */
29693     getEl : function(){
29694         return this.el;
29695     }
29696 });
29697
29698 /**
29699  * @class Roo.DialogManager
29700  * Provides global access to BasicDialogs that have been created and
29701  * support for z-indexing (layering) multiple open dialogs.
29702  */
29703 Roo.DialogManager = function(){
29704     var list = {};
29705     var accessList = [];
29706     var front = null;
29707
29708     // private
29709     var sortDialogs = function(d1, d2){
29710         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29711     };
29712
29713     // private
29714     var orderDialogs = function(){
29715         accessList.sort(sortDialogs);
29716         var seed = Roo.DialogManager.zseed;
29717         for(var i = 0, len = accessList.length; i < len; i++){
29718             var dlg = accessList[i];
29719             if(dlg){
29720                 dlg.setZIndex(seed + (i*10));
29721             }
29722         }
29723     };
29724
29725     return {
29726         /**
29727          * The starting z-index for BasicDialogs (defaults to 9000)
29728          * @type Number The z-index value
29729          */
29730         zseed : 9000,
29731
29732         // private
29733         register : function(dlg){
29734             list[dlg.id] = dlg;
29735             accessList.push(dlg);
29736         },
29737
29738         // private
29739         unregister : function(dlg){
29740             delete list[dlg.id];
29741             var i=0;
29742             var len=0;
29743             if(!accessList.indexOf){
29744                 for(  i = 0, len = accessList.length; i < len; i++){
29745                     if(accessList[i] == dlg){
29746                         accessList.splice(i, 1);
29747                         return;
29748                     }
29749                 }
29750             }else{
29751                  i = accessList.indexOf(dlg);
29752                 if(i != -1){
29753                     accessList.splice(i, 1);
29754                 }
29755             }
29756         },
29757
29758         /**
29759          * Gets a registered dialog by id
29760          * @param {String/Object} id The id of the dialog or a dialog
29761          * @return {Roo.BasicDialog} this
29762          */
29763         get : function(id){
29764             return typeof id == "object" ? id : list[id];
29765         },
29766
29767         /**
29768          * Brings the specified dialog to the front
29769          * @param {String/Object} dlg The id of the dialog or a dialog
29770          * @return {Roo.BasicDialog} this
29771          */
29772         bringToFront : function(dlg){
29773             dlg = this.get(dlg);
29774             if(dlg != front){
29775                 front = dlg;
29776                 dlg._lastAccess = new Date().getTime();
29777                 orderDialogs();
29778             }
29779             return dlg;
29780         },
29781
29782         /**
29783          * Sends the specified dialog to the back
29784          * @param {String/Object} dlg The id of the dialog or a dialog
29785          * @return {Roo.BasicDialog} this
29786          */
29787         sendToBack : function(dlg){
29788             dlg = this.get(dlg);
29789             dlg._lastAccess = -(new Date().getTime());
29790             orderDialogs();
29791             return dlg;
29792         },
29793
29794         /**
29795          * Hides all dialogs
29796          */
29797         hideAll : function(){
29798             for(var id in list){
29799                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29800                     list[id].hide();
29801                 }
29802             }
29803         }
29804     };
29805 }();
29806
29807 /**
29808  * @class Roo.LayoutDialog
29809  * @extends Roo.BasicDialog
29810  * Dialog which provides adjustments for working with a layout in a Dialog.
29811  * Add your necessary layout config options to the dialog's config.<br>
29812  * Example usage (including a nested layout):
29813  * <pre><code>
29814 if(!dialog){
29815     dialog = new Roo.LayoutDialog("download-dlg", {
29816         modal: true,
29817         width:600,
29818         height:450,
29819         shadow:true,
29820         minWidth:500,
29821         minHeight:350,
29822         autoTabs:true,
29823         proxyDrag:true,
29824         // layout config merges with the dialog config
29825         center:{
29826             tabPosition: "top",
29827             alwaysShowTabs: true
29828         }
29829     });
29830     dialog.addKeyListener(27, dialog.hide, dialog);
29831     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29832     dialog.addButton("Build It!", this.getDownload, this);
29833
29834     // we can even add nested layouts
29835     var innerLayout = new Roo.BorderLayout("dl-inner", {
29836         east: {
29837             initialSize: 200,
29838             autoScroll:true,
29839             split:true
29840         },
29841         center: {
29842             autoScroll:true
29843         }
29844     });
29845     innerLayout.beginUpdate();
29846     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29847     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29848     innerLayout.endUpdate(true);
29849
29850     var layout = dialog.getLayout();
29851     layout.beginUpdate();
29852     layout.add("center", new Roo.ContentPanel("standard-panel",
29853                         {title: "Download the Source", fitToFrame:true}));
29854     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29855                {title: "Build your own roo.js"}));
29856     layout.getRegion("center").showPanel(sp);
29857     layout.endUpdate();
29858 }
29859 </code></pre>
29860     * @constructor
29861     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29862     * @param {Object} config configuration options
29863   */
29864 Roo.LayoutDialog = function(el, cfg){
29865     
29866     var config=  cfg;
29867     if (typeof(cfg) == 'undefined') {
29868         config = Roo.apply({}, el);
29869         // not sure why we use documentElement here.. - it should always be body.
29870         // IE7 borks horribly if we use documentElement.
29871         // webkit also does not like documentElement - it creates a body element...
29872         el = Roo.get( document.body || document.documentElement ).createChild();
29873         //config.autoCreate = true;
29874     }
29875     
29876     
29877     config.autoTabs = false;
29878     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29879     this.body.setStyle({overflow:"hidden", position:"relative"});
29880     this.layout = new Roo.BorderLayout(this.body.dom, config);
29881     this.layout.monitorWindowResize = false;
29882     this.el.addClass("x-dlg-auto-layout");
29883     // fix case when center region overwrites center function
29884     this.center = Roo.BasicDialog.prototype.center;
29885     this.on("show", this.layout.layout, this.layout, true);
29886     if (config.items) {
29887         var xitems = config.items;
29888         delete config.items;
29889         Roo.each(xitems, this.addxtype, this);
29890     }
29891     
29892     
29893 };
29894 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29895     /**
29896      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29897      * @deprecated
29898      */
29899     endUpdate : function(){
29900         this.layout.endUpdate();
29901     },
29902
29903     /**
29904      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29905      *  @deprecated
29906      */
29907     beginUpdate : function(){
29908         this.layout.beginUpdate();
29909     },
29910
29911     /**
29912      * Get the BorderLayout for this dialog
29913      * @return {Roo.BorderLayout}
29914      */
29915     getLayout : function(){
29916         return this.layout;
29917     },
29918
29919     showEl : function(){
29920         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29921         if(Roo.isIE7){
29922             this.layout.layout();
29923         }
29924     },
29925
29926     // private
29927     // Use the syncHeightBeforeShow config option to control this automatically
29928     syncBodyHeight : function(){
29929         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29930         if(this.layout){this.layout.layout();}
29931     },
29932     
29933       /**
29934      * Add an xtype element (actually adds to the layout.)
29935      * @return {Object} xdata xtype object data.
29936      */
29937     
29938     addxtype : function(c) {
29939         return this.layout.addxtype(c);
29940     }
29941 });/*
29942  * Based on:
29943  * Ext JS Library 1.1.1
29944  * Copyright(c) 2006-2007, Ext JS, LLC.
29945  *
29946  * Originally Released Under LGPL - original licence link has changed is not relivant.
29947  *
29948  * Fork - LGPL
29949  * <script type="text/javascript">
29950  */
29951  
29952 /**
29953  * @class Roo.MessageBox
29954  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29955  * Example usage:
29956  *<pre><code>
29957 // Basic alert:
29958 Roo.Msg.alert('Status', 'Changes saved successfully.');
29959
29960 // Prompt for user data:
29961 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29962     if (btn == 'ok'){
29963         // process text value...
29964     }
29965 });
29966
29967 // Show a dialog using config options:
29968 Roo.Msg.show({
29969    title:'Save Changes?',
29970    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29971    buttons: Roo.Msg.YESNOCANCEL,
29972    fn: processResult,
29973    animEl: 'elId'
29974 });
29975 </code></pre>
29976  * @singleton
29977  */
29978 Roo.MessageBox = function(){
29979     var dlg, opt, mask, waitTimer;
29980     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29981     var buttons, activeTextEl, bwidth;
29982
29983     // private
29984     var handleButton = function(button){
29985         dlg.hide();
29986         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29987     };
29988
29989     // private
29990     var handleHide = function(){
29991         if(opt && opt.cls){
29992             dlg.el.removeClass(opt.cls);
29993         }
29994         if(waitTimer){
29995             Roo.TaskMgr.stop(waitTimer);
29996             waitTimer = null;
29997         }
29998     };
29999
30000     // private
30001     var updateButtons = function(b){
30002         var width = 0;
30003         if(!b){
30004             buttons["ok"].hide();
30005             buttons["cancel"].hide();
30006             buttons["yes"].hide();
30007             buttons["no"].hide();
30008             dlg.footer.dom.style.display = 'none';
30009             return width;
30010         }
30011         dlg.footer.dom.style.display = '';
30012         for(var k in buttons){
30013             if(typeof buttons[k] != "function"){
30014                 if(b[k]){
30015                     buttons[k].show();
30016                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30017                     width += buttons[k].el.getWidth()+15;
30018                 }else{
30019                     buttons[k].hide();
30020                 }
30021             }
30022         }
30023         return width;
30024     };
30025
30026     // private
30027     var handleEsc = function(d, k, e){
30028         if(opt && opt.closable !== false){
30029             dlg.hide();
30030         }
30031         if(e){
30032             e.stopEvent();
30033         }
30034     };
30035
30036     return {
30037         /**
30038          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30039          * @return {Roo.BasicDialog} The BasicDialog element
30040          */
30041         getDialog : function(){
30042            if(!dlg){
30043                 dlg = new Roo.BasicDialog("x-msg-box", {
30044                     autoCreate : true,
30045                     shadow: true,
30046                     draggable: true,
30047                     resizable:false,
30048                     constraintoviewport:false,
30049                     fixedcenter:true,
30050                     collapsible : false,
30051                     shim:true,
30052                     modal: true,
30053                     width:400, height:100,
30054                     buttonAlign:"center",
30055                     closeClick : function(){
30056                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30057                             handleButton("no");
30058                         }else{
30059                             handleButton("cancel");
30060                         }
30061                     }
30062                 });
30063                 dlg.on("hide", handleHide);
30064                 mask = dlg.mask;
30065                 dlg.addKeyListener(27, handleEsc);
30066                 buttons = {};
30067                 var bt = this.buttonText;
30068                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30069                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30070                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30071                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30072                 bodyEl = dlg.body.createChild({
30073
30074                     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>'
30075                 });
30076                 msgEl = bodyEl.dom.firstChild;
30077                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30078                 textboxEl.enableDisplayMode();
30079                 textboxEl.addKeyListener([10,13], function(){
30080                     if(dlg.isVisible() && opt && opt.buttons){
30081                         if(opt.buttons.ok){
30082                             handleButton("ok");
30083                         }else if(opt.buttons.yes){
30084                             handleButton("yes");
30085                         }
30086                     }
30087                 });
30088                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30089                 textareaEl.enableDisplayMode();
30090                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30091                 progressEl.enableDisplayMode();
30092                 var pf = progressEl.dom.firstChild;
30093                 if (pf) {
30094                     pp = Roo.get(pf.firstChild);
30095                     pp.setHeight(pf.offsetHeight);
30096                 }
30097                 
30098             }
30099             return dlg;
30100         },
30101
30102         /**
30103          * Updates the message box body text
30104          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30105          * the XHTML-compliant non-breaking space character '&amp;#160;')
30106          * @return {Roo.MessageBox} This message box
30107          */
30108         updateText : function(text){
30109             if(!dlg.isVisible() && !opt.width){
30110                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30111             }
30112             msgEl.innerHTML = text || '&#160;';
30113       
30114             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30115             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30116             var w = Math.max(
30117                     Math.min(opt.width || cw , this.maxWidth), 
30118                     Math.max(opt.minWidth || this.minWidth, bwidth)
30119             );
30120             if(opt.prompt){
30121                 activeTextEl.setWidth(w);
30122             }
30123             if(dlg.isVisible()){
30124                 dlg.fixedcenter = false;
30125             }
30126             // to big, make it scroll. = But as usual stupid IE does not support
30127             // !important..
30128             
30129             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30130                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30131                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30132             } else {
30133                 bodyEl.dom.style.height = '';
30134                 bodyEl.dom.style.overflowY = '';
30135             }
30136             if (cw > w) {
30137                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30138             } else {
30139                 bodyEl.dom.style.overflowX = '';
30140             }
30141             
30142             dlg.setContentSize(w, bodyEl.getHeight());
30143             if(dlg.isVisible()){
30144                 dlg.fixedcenter = true;
30145             }
30146             return this;
30147         },
30148
30149         /**
30150          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30151          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30152          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30153          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30154          * @return {Roo.MessageBox} This message box
30155          */
30156         updateProgress : function(value, text){
30157             if(text){
30158                 this.updateText(text);
30159             }
30160             if (pp) { // weird bug on my firefox - for some reason this is not defined
30161                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30162             }
30163             return this;
30164         },        
30165
30166         /**
30167          * Returns true if the message box is currently displayed
30168          * @return {Boolean} True if the message box is visible, else false
30169          */
30170         isVisible : function(){
30171             return dlg && dlg.isVisible();  
30172         },
30173
30174         /**
30175          * Hides the message box if it is displayed
30176          */
30177         hide : function(){
30178             if(this.isVisible()){
30179                 dlg.hide();
30180             }  
30181         },
30182
30183         /**
30184          * Displays a new message box, or reinitializes an existing message box, based on the config options
30185          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30186          * The following config object properties are supported:
30187          * <pre>
30188 Property    Type             Description
30189 ----------  ---------------  ------------------------------------------------------------------------------------
30190 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30191                                    closes (defaults to undefined)
30192 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30193                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30194 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30195                                    progress and wait dialogs will ignore this property and always hide the
30196                                    close button as they can only be closed programmatically.
30197 cls               String           A custom CSS class to apply to the message box element
30198 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30199                                    displayed (defaults to 75)
30200 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30201                                    function will be btn (the name of the button that was clicked, if applicable,
30202                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30203                                    Progress and wait dialogs will ignore this option since they do not respond to
30204                                    user actions and can only be closed programmatically, so any required function
30205                                    should be called by the same code after it closes the dialog.
30206 icon              String           A CSS class that provides a background image to be used as an icon for
30207                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30208 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30209 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30210 modal             Boolean          False to allow user interaction with the page while the message box is
30211                                    displayed (defaults to true)
30212 msg               String           A string that will replace the existing message box body text (defaults
30213                                    to the XHTML-compliant non-breaking space character '&#160;')
30214 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30215 progress          Boolean          True to display a progress bar (defaults to false)
30216 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30217 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30218 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30219 title             String           The title text
30220 value             String           The string value to set into the active textbox element if displayed
30221 wait              Boolean          True to display a progress bar (defaults to false)
30222 width             Number           The width of the dialog in pixels
30223 </pre>
30224          *
30225          * Example usage:
30226          * <pre><code>
30227 Roo.Msg.show({
30228    title: 'Address',
30229    msg: 'Please enter your address:',
30230    width: 300,
30231    buttons: Roo.MessageBox.OKCANCEL,
30232    multiline: true,
30233    fn: saveAddress,
30234    animEl: 'addAddressBtn'
30235 });
30236 </code></pre>
30237          * @param {Object} config Configuration options
30238          * @return {Roo.MessageBox} This message box
30239          */
30240         show : function(options)
30241         {
30242             
30243             // this causes nightmares if you show one dialog after another
30244             // especially on callbacks..
30245              
30246             if(this.isVisible()){
30247                 
30248                 this.hide();
30249                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30250                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30251                 Roo.log("New Dialog Message:" +  options.msg )
30252                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30253                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30254                 
30255             }
30256             var d = this.getDialog();
30257             opt = options;
30258             d.setTitle(opt.title || "&#160;");
30259             d.close.setDisplayed(opt.closable !== false);
30260             activeTextEl = textboxEl;
30261             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30262             if(opt.prompt){
30263                 if(opt.multiline){
30264                     textboxEl.hide();
30265                     textareaEl.show();
30266                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30267                         opt.multiline : this.defaultTextHeight);
30268                     activeTextEl = textareaEl;
30269                 }else{
30270                     textboxEl.show();
30271                     textareaEl.hide();
30272                 }
30273             }else{
30274                 textboxEl.hide();
30275                 textareaEl.hide();
30276             }
30277             progressEl.setDisplayed(opt.progress === true);
30278             this.updateProgress(0);
30279             activeTextEl.dom.value = opt.value || "";
30280             if(opt.prompt){
30281                 dlg.setDefaultButton(activeTextEl);
30282             }else{
30283                 var bs = opt.buttons;
30284                 var db = null;
30285                 if(bs && bs.ok){
30286                     db = buttons["ok"];
30287                 }else if(bs && bs.yes){
30288                     db = buttons["yes"];
30289                 }
30290                 dlg.setDefaultButton(db);
30291             }
30292             bwidth = updateButtons(opt.buttons);
30293             this.updateText(opt.msg);
30294             if(opt.cls){
30295                 d.el.addClass(opt.cls);
30296             }
30297             d.proxyDrag = opt.proxyDrag === true;
30298             d.modal = opt.modal !== false;
30299             d.mask = opt.modal !== false ? mask : false;
30300             if(!d.isVisible()){
30301                 // force it to the end of the z-index stack so it gets a cursor in FF
30302                 document.body.appendChild(dlg.el.dom);
30303                 d.animateTarget = null;
30304                 d.show(options.animEl);
30305             }
30306             return this;
30307         },
30308
30309         /**
30310          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30311          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30312          * and closing the message box when the process is complete.
30313          * @param {String} title The title bar text
30314          * @param {String} msg The message box body text
30315          * @return {Roo.MessageBox} This message box
30316          */
30317         progress : function(title, msg){
30318             this.show({
30319                 title : title,
30320                 msg : msg,
30321                 buttons: false,
30322                 progress:true,
30323                 closable:false,
30324                 minWidth: this.minProgressWidth,
30325                 modal : true
30326             });
30327             return this;
30328         },
30329
30330         /**
30331          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30332          * If a callback function is passed it will be called after the user clicks the button, and the
30333          * id of the button that was clicked will be passed as the only parameter to the callback
30334          * (could also be the top-right close button).
30335          * @param {String} title The title bar text
30336          * @param {String} msg The message box body text
30337          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30338          * @param {Object} scope (optional) The scope of the callback function
30339          * @return {Roo.MessageBox} This message box
30340          */
30341         alert : function(title, msg, fn, scope){
30342             this.show({
30343                 title : title,
30344                 msg : msg,
30345                 buttons: this.OK,
30346                 fn: fn,
30347                 scope : scope,
30348                 modal : true
30349             });
30350             return this;
30351         },
30352
30353         /**
30354          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30355          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30356          * You are responsible for closing the message box when the process is complete.
30357          * @param {String} msg The message box body text
30358          * @param {String} title (optional) The title bar text
30359          * @return {Roo.MessageBox} This message box
30360          */
30361         wait : function(msg, title){
30362             this.show({
30363                 title : title,
30364                 msg : msg,
30365                 buttons: false,
30366                 closable:false,
30367                 progress:true,
30368                 modal:true,
30369                 width:300,
30370                 wait:true
30371             });
30372             waitTimer = Roo.TaskMgr.start({
30373                 run: function(i){
30374                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30375                 },
30376                 interval: 1000
30377             });
30378             return this;
30379         },
30380
30381         /**
30382          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30383          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30384          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30385          * @param {String} title The title bar text
30386          * @param {String} msg The message box body text
30387          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30388          * @param {Object} scope (optional) The scope of the callback function
30389          * @return {Roo.MessageBox} This message box
30390          */
30391         confirm : function(title, msg, fn, scope){
30392             this.show({
30393                 title : title,
30394                 msg : msg,
30395                 buttons: this.YESNO,
30396                 fn: fn,
30397                 scope : scope,
30398                 modal : true
30399             });
30400             return this;
30401         },
30402
30403         /**
30404          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30405          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30406          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30407          * (could also be the top-right close button) and the text that was entered will be passed as the two
30408          * parameters to the callback.
30409          * @param {String} title The title bar text
30410          * @param {String} msg The message box body text
30411          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30412          * @param {Object} scope (optional) The scope of the callback function
30413          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30414          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30415          * @return {Roo.MessageBox} This message box
30416          */
30417         prompt : function(title, msg, fn, scope, multiline){
30418             this.show({
30419                 title : title,
30420                 msg : msg,
30421                 buttons: this.OKCANCEL,
30422                 fn: fn,
30423                 minWidth:250,
30424                 scope : scope,
30425                 prompt:true,
30426                 multiline: multiline,
30427                 modal : true
30428             });
30429             return this;
30430         },
30431
30432         /**
30433          * Button config that displays a single OK button
30434          * @type Object
30435          */
30436         OK : {ok:true},
30437         /**
30438          * Button config that displays Yes and No buttons
30439          * @type Object
30440          */
30441         YESNO : {yes:true, no:true},
30442         /**
30443          * Button config that displays OK and Cancel buttons
30444          * @type Object
30445          */
30446         OKCANCEL : {ok:true, cancel:true},
30447         /**
30448          * Button config that displays Yes, No and Cancel buttons
30449          * @type Object
30450          */
30451         YESNOCANCEL : {yes:true, no:true, cancel:true},
30452
30453         /**
30454          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30455          * @type Number
30456          */
30457         defaultTextHeight : 75,
30458         /**
30459          * The maximum width in pixels of the message box (defaults to 600)
30460          * @type Number
30461          */
30462         maxWidth : 600,
30463         /**
30464          * The minimum width in pixels of the message box (defaults to 100)
30465          * @type Number
30466          */
30467         minWidth : 100,
30468         /**
30469          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30470          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30471          * @type Number
30472          */
30473         minProgressWidth : 250,
30474         /**
30475          * An object containing the default button text strings that can be overriden for localized language support.
30476          * Supported properties are: ok, cancel, yes and no.
30477          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30478          * @type Object
30479          */
30480         buttonText : {
30481             ok : "OK",
30482             cancel : "Cancel",
30483             yes : "Yes",
30484             no : "No"
30485         }
30486     };
30487 }();
30488
30489 /**
30490  * Shorthand for {@link Roo.MessageBox}
30491  */
30492 Roo.Msg = Roo.MessageBox;/*
30493  * Based on:
30494  * Ext JS Library 1.1.1
30495  * Copyright(c) 2006-2007, Ext JS, LLC.
30496  *
30497  * Originally Released Under LGPL - original licence link has changed is not relivant.
30498  *
30499  * Fork - LGPL
30500  * <script type="text/javascript">
30501  */
30502 /**
30503  * @class Roo.QuickTips
30504  * Provides attractive and customizable tooltips for any element.
30505  * @singleton
30506  */
30507 Roo.QuickTips = function(){
30508     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30509     var ce, bd, xy, dd;
30510     var visible = false, disabled = true, inited = false;
30511     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30512     
30513     var onOver = function(e){
30514         if(disabled){
30515             return;
30516         }
30517         var t = e.getTarget();
30518         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30519             return;
30520         }
30521         if(ce && t == ce.el){
30522             clearTimeout(hideProc);
30523             return;
30524         }
30525         if(t && tagEls[t.id]){
30526             tagEls[t.id].el = t;
30527             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30528             return;
30529         }
30530         var ttp, et = Roo.fly(t);
30531         var ns = cfg.namespace;
30532         if(tm.interceptTitles && t.title){
30533             ttp = t.title;
30534             t.qtip = ttp;
30535             t.removeAttribute("title");
30536             e.preventDefault();
30537         }else{
30538             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30539         }
30540         if(ttp){
30541             showProc = show.defer(tm.showDelay, tm, [{
30542                 el: t, 
30543                 text: ttp, 
30544                 width: et.getAttributeNS(ns, cfg.width),
30545                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30546                 title: et.getAttributeNS(ns, cfg.title),
30547                     cls: et.getAttributeNS(ns, cfg.cls)
30548             }]);
30549         }
30550     };
30551     
30552     var onOut = function(e){
30553         clearTimeout(showProc);
30554         var t = e.getTarget();
30555         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30556             hideProc = setTimeout(hide, tm.hideDelay);
30557         }
30558     };
30559     
30560     var onMove = function(e){
30561         if(disabled){
30562             return;
30563         }
30564         xy = e.getXY();
30565         xy[1] += 18;
30566         if(tm.trackMouse && ce){
30567             el.setXY(xy);
30568         }
30569     };
30570     
30571     var onDown = function(e){
30572         clearTimeout(showProc);
30573         clearTimeout(hideProc);
30574         if(!e.within(el)){
30575             if(tm.hideOnClick){
30576                 hide();
30577                 tm.disable();
30578                 tm.enable.defer(100, tm);
30579             }
30580         }
30581     };
30582     
30583     var getPad = function(){
30584         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30585     };
30586
30587     var show = function(o){
30588         if(disabled){
30589             return;
30590         }
30591         clearTimeout(dismissProc);
30592         ce = o;
30593         if(removeCls){ // in case manually hidden
30594             el.removeClass(removeCls);
30595             removeCls = null;
30596         }
30597         if(ce.cls){
30598             el.addClass(ce.cls);
30599             removeCls = ce.cls;
30600         }
30601         if(ce.title){
30602             tipTitle.update(ce.title);
30603             tipTitle.show();
30604         }else{
30605             tipTitle.update('');
30606             tipTitle.hide();
30607         }
30608         el.dom.style.width  = tm.maxWidth+'px';
30609         //tipBody.dom.style.width = '';
30610         tipBodyText.update(o.text);
30611         var p = getPad(), w = ce.width;
30612         if(!w){
30613             var td = tipBodyText.dom;
30614             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30615             if(aw > tm.maxWidth){
30616                 w = tm.maxWidth;
30617             }else if(aw < tm.minWidth){
30618                 w = tm.minWidth;
30619             }else{
30620                 w = aw;
30621             }
30622         }
30623         //tipBody.setWidth(w);
30624         el.setWidth(parseInt(w, 10) + p);
30625         if(ce.autoHide === false){
30626             close.setDisplayed(true);
30627             if(dd){
30628                 dd.unlock();
30629             }
30630         }else{
30631             close.setDisplayed(false);
30632             if(dd){
30633                 dd.lock();
30634             }
30635         }
30636         if(xy){
30637             el.avoidY = xy[1]-18;
30638             el.setXY(xy);
30639         }
30640         if(tm.animate){
30641             el.setOpacity(.1);
30642             el.setStyle("visibility", "visible");
30643             el.fadeIn({callback: afterShow});
30644         }else{
30645             afterShow();
30646         }
30647     };
30648     
30649     var afterShow = function(){
30650         if(ce){
30651             el.show();
30652             esc.enable();
30653             if(tm.autoDismiss && ce.autoHide !== false){
30654                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30655             }
30656         }
30657     };
30658     
30659     var hide = function(noanim){
30660         clearTimeout(dismissProc);
30661         clearTimeout(hideProc);
30662         ce = null;
30663         if(el.isVisible()){
30664             esc.disable();
30665             if(noanim !== true && tm.animate){
30666                 el.fadeOut({callback: afterHide});
30667             }else{
30668                 afterHide();
30669             } 
30670         }
30671     };
30672     
30673     var afterHide = function(){
30674         el.hide();
30675         if(removeCls){
30676             el.removeClass(removeCls);
30677             removeCls = null;
30678         }
30679     };
30680     
30681     return {
30682         /**
30683         * @cfg {Number} minWidth
30684         * The minimum width of the quick tip (defaults to 40)
30685         */
30686        minWidth : 40,
30687         /**
30688         * @cfg {Number} maxWidth
30689         * The maximum width of the quick tip (defaults to 300)
30690         */
30691        maxWidth : 300,
30692         /**
30693         * @cfg {Boolean} interceptTitles
30694         * True to automatically use the element's DOM title value if available (defaults to false)
30695         */
30696        interceptTitles : false,
30697         /**
30698         * @cfg {Boolean} trackMouse
30699         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30700         */
30701        trackMouse : false,
30702         /**
30703         * @cfg {Boolean} hideOnClick
30704         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30705         */
30706        hideOnClick : true,
30707         /**
30708         * @cfg {Number} showDelay
30709         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30710         */
30711        showDelay : 500,
30712         /**
30713         * @cfg {Number} hideDelay
30714         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30715         */
30716        hideDelay : 200,
30717         /**
30718         * @cfg {Boolean} autoHide
30719         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30720         * Used in conjunction with hideDelay.
30721         */
30722        autoHide : true,
30723         /**
30724         * @cfg {Boolean}
30725         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30726         * (defaults to true).  Used in conjunction with autoDismissDelay.
30727         */
30728        autoDismiss : true,
30729         /**
30730         * @cfg {Number}
30731         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30732         */
30733        autoDismissDelay : 5000,
30734        /**
30735         * @cfg {Boolean} animate
30736         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30737         */
30738        animate : false,
30739
30740        /**
30741         * @cfg {String} title
30742         * Title text to display (defaults to '').  This can be any valid HTML markup.
30743         */
30744         title: '',
30745        /**
30746         * @cfg {String} text
30747         * Body text to display (defaults to '').  This can be any valid HTML markup.
30748         */
30749         text : '',
30750        /**
30751         * @cfg {String} cls
30752         * A CSS class to apply to the base quick tip element (defaults to '').
30753         */
30754         cls : '',
30755        /**
30756         * @cfg {Number} width
30757         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30758         * minWidth or maxWidth.
30759         */
30760         width : null,
30761
30762     /**
30763      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30764      * or display QuickTips in a page.
30765      */
30766        init : function(){
30767           tm = Roo.QuickTips;
30768           cfg = tm.tagConfig;
30769           if(!inited){
30770               if(!Roo.isReady){ // allow calling of init() before onReady
30771                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30772                   return;
30773               }
30774               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30775               el.fxDefaults = {stopFx: true};
30776               // maximum custom styling
30777               //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>');
30778               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>');              
30779               tipTitle = el.child('h3');
30780               tipTitle.enableDisplayMode("block");
30781               tipBody = el.child('div.x-tip-bd');
30782               tipBodyText = el.child('div.x-tip-bd-inner');
30783               //bdLeft = el.child('div.x-tip-bd-left');
30784               //bdRight = el.child('div.x-tip-bd-right');
30785               close = el.child('div.x-tip-close');
30786               close.enableDisplayMode("block");
30787               close.on("click", hide);
30788               var d = Roo.get(document);
30789               d.on("mousedown", onDown);
30790               d.on("mouseover", onOver);
30791               d.on("mouseout", onOut);
30792               d.on("mousemove", onMove);
30793               esc = d.addKeyListener(27, hide);
30794               esc.disable();
30795               if(Roo.dd.DD){
30796                   dd = el.initDD("default", null, {
30797                       onDrag : function(){
30798                           el.sync();  
30799                       }
30800                   });
30801                   dd.setHandleElId(tipTitle.id);
30802                   dd.lock();
30803               }
30804               inited = true;
30805           }
30806           this.enable(); 
30807        },
30808
30809     /**
30810      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30811      * are supported:
30812      * <pre>
30813 Property    Type                   Description
30814 ----------  ---------------------  ------------------------------------------------------------------------
30815 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30816      * </ul>
30817      * @param {Object} config The config object
30818      */
30819        register : function(config){
30820            var cs = config instanceof Array ? config : arguments;
30821            for(var i = 0, len = cs.length; i < len; i++) {
30822                var c = cs[i];
30823                var target = c.target;
30824                if(target){
30825                    if(target instanceof Array){
30826                        for(var j = 0, jlen = target.length; j < jlen; j++){
30827                            tagEls[target[j]] = c;
30828                        }
30829                    }else{
30830                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30831                    }
30832                }
30833            }
30834        },
30835
30836     /**
30837      * Removes this quick tip from its element and destroys it.
30838      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30839      */
30840        unregister : function(el){
30841            delete tagEls[Roo.id(el)];
30842        },
30843
30844     /**
30845      * Enable this quick tip.
30846      */
30847        enable : function(){
30848            if(inited && disabled){
30849                locks.pop();
30850                if(locks.length < 1){
30851                    disabled = false;
30852                }
30853            }
30854        },
30855
30856     /**
30857      * Disable this quick tip.
30858      */
30859        disable : function(){
30860           disabled = true;
30861           clearTimeout(showProc);
30862           clearTimeout(hideProc);
30863           clearTimeout(dismissProc);
30864           if(ce){
30865               hide(true);
30866           }
30867           locks.push(1);
30868        },
30869
30870     /**
30871      * Returns true if the quick tip is enabled, else false.
30872      */
30873        isEnabled : function(){
30874             return !disabled;
30875        },
30876
30877         // private
30878        tagConfig : {
30879            namespace : "ext",
30880            attribute : "qtip",
30881            width : "width",
30882            target : "target",
30883            title : "qtitle",
30884            hide : "hide",
30885            cls : "qclass"
30886        }
30887    };
30888 }();
30889
30890 // backwards compat
30891 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30892  * Based on:
30893  * Ext JS Library 1.1.1
30894  * Copyright(c) 2006-2007, Ext JS, LLC.
30895  *
30896  * Originally Released Under LGPL - original licence link has changed is not relivant.
30897  *
30898  * Fork - LGPL
30899  * <script type="text/javascript">
30900  */
30901  
30902
30903 /**
30904  * @class Roo.tree.TreePanel
30905  * @extends Roo.data.Tree
30906
30907  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30908  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30909  * @cfg {Boolean} enableDD true to enable drag and drop
30910  * @cfg {Boolean} enableDrag true to enable just drag
30911  * @cfg {Boolean} enableDrop true to enable just drop
30912  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30913  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30914  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30915  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30916  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30917  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30918  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30919  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30920  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30921  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30922  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30923  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30924  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30925  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30926  * @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>
30927  * @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>
30928  * 
30929  * @constructor
30930  * @param {String/HTMLElement/Element} el The container element
30931  * @param {Object} config
30932  */
30933 Roo.tree.TreePanel = function(el, config){
30934     var root = false;
30935     var loader = false;
30936     if (config.root) {
30937         root = config.root;
30938         delete config.root;
30939     }
30940     if (config.loader) {
30941         loader = config.loader;
30942         delete config.loader;
30943     }
30944     
30945     Roo.apply(this, config);
30946     Roo.tree.TreePanel.superclass.constructor.call(this);
30947     this.el = Roo.get(el);
30948     this.el.addClass('x-tree');
30949     //console.log(root);
30950     if (root) {
30951         this.setRootNode( Roo.factory(root, Roo.tree));
30952     }
30953     if (loader) {
30954         this.loader = Roo.factory(loader, Roo.tree);
30955     }
30956    /**
30957     * Read-only. The id of the container element becomes this TreePanel's id.
30958     */
30959     this.id = this.el.id;
30960     this.addEvents({
30961         /**
30962         * @event beforeload
30963         * Fires before a node is loaded, return false to cancel
30964         * @param {Node} node The node being loaded
30965         */
30966         "beforeload" : true,
30967         /**
30968         * @event load
30969         * Fires when a node is loaded
30970         * @param {Node} node The node that was loaded
30971         */
30972         "load" : true,
30973         /**
30974         * @event textchange
30975         * Fires when the text for a node is changed
30976         * @param {Node} node The node
30977         * @param {String} text The new text
30978         * @param {String} oldText The old text
30979         */
30980         "textchange" : true,
30981         /**
30982         * @event beforeexpand
30983         * Fires before a node is expanded, return false to cancel.
30984         * @param {Node} node The node
30985         * @param {Boolean} deep
30986         * @param {Boolean} anim
30987         */
30988         "beforeexpand" : true,
30989         /**
30990         * @event beforecollapse
30991         * Fires before a node is collapsed, return false to cancel.
30992         * @param {Node} node The node
30993         * @param {Boolean} deep
30994         * @param {Boolean} anim
30995         */
30996         "beforecollapse" : true,
30997         /**
30998         * @event expand
30999         * Fires when a node is expanded
31000         * @param {Node} node The node
31001         */
31002         "expand" : true,
31003         /**
31004         * @event disabledchange
31005         * Fires when the disabled status of a node changes
31006         * @param {Node} node The node
31007         * @param {Boolean} disabled
31008         */
31009         "disabledchange" : true,
31010         /**
31011         * @event collapse
31012         * Fires when a node is collapsed
31013         * @param {Node} node The node
31014         */
31015         "collapse" : true,
31016         /**
31017         * @event beforeclick
31018         * Fires before click processing on a node. Return false to cancel the default action.
31019         * @param {Node} node The node
31020         * @param {Roo.EventObject} e The event object
31021         */
31022         "beforeclick":true,
31023         /**
31024         * @event checkchange
31025         * Fires when a node with a checkbox's checked property changes
31026         * @param {Node} this This node
31027         * @param {Boolean} checked
31028         */
31029         "checkchange":true,
31030         /**
31031         * @event click
31032         * Fires when a node is clicked
31033         * @param {Node} node The node
31034         * @param {Roo.EventObject} e The event object
31035         */
31036         "click":true,
31037         /**
31038         * @event dblclick
31039         * Fires when a node is double clicked
31040         * @param {Node} node The node
31041         * @param {Roo.EventObject} e The event object
31042         */
31043         "dblclick":true,
31044         /**
31045         * @event contextmenu
31046         * Fires when a node is right clicked
31047         * @param {Node} node The node
31048         * @param {Roo.EventObject} e The event object
31049         */
31050         "contextmenu":true,
31051         /**
31052         * @event beforechildrenrendered
31053         * Fires right before the child nodes for a node are rendered
31054         * @param {Node} node The node
31055         */
31056         "beforechildrenrendered":true,
31057         /**
31058         * @event startdrag
31059         * Fires when a node starts being dragged
31060         * @param {Roo.tree.TreePanel} this
31061         * @param {Roo.tree.TreeNode} node
31062         * @param {event} e The raw browser event
31063         */ 
31064        "startdrag" : true,
31065        /**
31066         * @event enddrag
31067         * Fires when a drag operation is complete
31068         * @param {Roo.tree.TreePanel} this
31069         * @param {Roo.tree.TreeNode} node
31070         * @param {event} e The raw browser event
31071         */
31072        "enddrag" : true,
31073        /**
31074         * @event dragdrop
31075         * Fires when a dragged node is dropped on a valid DD target
31076         * @param {Roo.tree.TreePanel} this
31077         * @param {Roo.tree.TreeNode} node
31078         * @param {DD} dd The dd it was dropped on
31079         * @param {event} e The raw browser event
31080         */
31081        "dragdrop" : true,
31082        /**
31083         * @event beforenodedrop
31084         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31085         * passed to handlers has the following properties:<br />
31086         * <ul style="padding:5px;padding-left:16px;">
31087         * <li>tree - The TreePanel</li>
31088         * <li>target - The node being targeted for the drop</li>
31089         * <li>data - The drag data from the drag source</li>
31090         * <li>point - The point of the drop - append, above or below</li>
31091         * <li>source - The drag source</li>
31092         * <li>rawEvent - Raw mouse event</li>
31093         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31094         * to be inserted by setting them on this object.</li>
31095         * <li>cancel - Set this to true to cancel the drop.</li>
31096         * </ul>
31097         * @param {Object} dropEvent
31098         */
31099        "beforenodedrop" : true,
31100        /**
31101         * @event nodedrop
31102         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31103         * passed to handlers has the following properties:<br />
31104         * <ul style="padding:5px;padding-left:16px;">
31105         * <li>tree - The TreePanel</li>
31106         * <li>target - The node being targeted for the drop</li>
31107         * <li>data - The drag data from the drag source</li>
31108         * <li>point - The point of the drop - append, above or below</li>
31109         * <li>source - The drag source</li>
31110         * <li>rawEvent - Raw mouse event</li>
31111         * <li>dropNode - Dropped node(s).</li>
31112         * </ul>
31113         * @param {Object} dropEvent
31114         */
31115        "nodedrop" : true,
31116         /**
31117         * @event nodedragover
31118         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31119         * passed to handlers has the following properties:<br />
31120         * <ul style="padding:5px;padding-left:16px;">
31121         * <li>tree - The TreePanel</li>
31122         * <li>target - The node being targeted for the drop</li>
31123         * <li>data - The drag data from the drag source</li>
31124         * <li>point - The point of the drop - append, above or below</li>
31125         * <li>source - The drag source</li>
31126         * <li>rawEvent - Raw mouse event</li>
31127         * <li>dropNode - Drop node(s) provided by the source.</li>
31128         * <li>cancel - Set this to true to signal drop not allowed.</li>
31129         * </ul>
31130         * @param {Object} dragOverEvent
31131         */
31132        "nodedragover" : true
31133         
31134     });
31135     if(this.singleExpand){
31136        this.on("beforeexpand", this.restrictExpand, this);
31137     }
31138     if (this.editor) {
31139         this.editor.tree = this;
31140         this.editor = Roo.factory(this.editor, Roo.tree);
31141     }
31142     
31143     if (this.selModel) {
31144         this.selModel = Roo.factory(this.selModel, Roo.tree);
31145     }
31146    
31147 };
31148 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31149     rootVisible : true,
31150     animate: Roo.enableFx,
31151     lines : true,
31152     enableDD : false,
31153     hlDrop : Roo.enableFx,
31154   
31155     renderer: false,
31156     
31157     rendererTip: false,
31158     // private
31159     restrictExpand : function(node){
31160         var p = node.parentNode;
31161         if(p){
31162             if(p.expandedChild && p.expandedChild.parentNode == p){
31163                 p.expandedChild.collapse();
31164             }
31165             p.expandedChild = node;
31166         }
31167     },
31168
31169     // private override
31170     setRootNode : function(node){
31171         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31172         if(!this.rootVisible){
31173             node.ui = new Roo.tree.RootTreeNodeUI(node);
31174         }
31175         return node;
31176     },
31177
31178     /**
31179      * Returns the container element for this TreePanel
31180      */
31181     getEl : function(){
31182         return this.el;
31183     },
31184
31185     /**
31186      * Returns the default TreeLoader for this TreePanel
31187      */
31188     getLoader : function(){
31189         return this.loader;
31190     },
31191
31192     /**
31193      * Expand all nodes
31194      */
31195     expandAll : function(){
31196         this.root.expand(true);
31197     },
31198
31199     /**
31200      * Collapse all nodes
31201      */
31202     collapseAll : function(){
31203         this.root.collapse(true);
31204     },
31205
31206     /**
31207      * Returns the selection model used by this TreePanel
31208      */
31209     getSelectionModel : function(){
31210         if(!this.selModel){
31211             this.selModel = new Roo.tree.DefaultSelectionModel();
31212         }
31213         return this.selModel;
31214     },
31215
31216     /**
31217      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31218      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31219      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31220      * @return {Array}
31221      */
31222     getChecked : function(a, startNode){
31223         startNode = startNode || this.root;
31224         var r = [];
31225         var f = function(){
31226             if(this.attributes.checked){
31227                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31228             }
31229         }
31230         startNode.cascade(f);
31231         return r;
31232     },
31233
31234     /**
31235      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31236      * @param {String} path
31237      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31238      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31239      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31240      */
31241     expandPath : function(path, attr, callback){
31242         attr = attr || "id";
31243         var keys = path.split(this.pathSeparator);
31244         var curNode = this.root;
31245         if(curNode.attributes[attr] != keys[1]){ // invalid root
31246             if(callback){
31247                 callback(false, null);
31248             }
31249             return;
31250         }
31251         var index = 1;
31252         var f = function(){
31253             if(++index == keys.length){
31254                 if(callback){
31255                     callback(true, curNode);
31256                 }
31257                 return;
31258             }
31259             var c = curNode.findChild(attr, keys[index]);
31260             if(!c){
31261                 if(callback){
31262                     callback(false, curNode);
31263                 }
31264                 return;
31265             }
31266             curNode = c;
31267             c.expand(false, false, f);
31268         };
31269         curNode.expand(false, false, f);
31270     },
31271
31272     /**
31273      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31274      * @param {String} path
31275      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31276      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31277      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31278      */
31279     selectPath : function(path, attr, callback){
31280         attr = attr || "id";
31281         var keys = path.split(this.pathSeparator);
31282         var v = keys.pop();
31283         if(keys.length > 0){
31284             var f = function(success, node){
31285                 if(success && node){
31286                     var n = node.findChild(attr, v);
31287                     if(n){
31288                         n.select();
31289                         if(callback){
31290                             callback(true, n);
31291                         }
31292                     }else if(callback){
31293                         callback(false, n);
31294                     }
31295                 }else{
31296                     if(callback){
31297                         callback(false, n);
31298                     }
31299                 }
31300             };
31301             this.expandPath(keys.join(this.pathSeparator), attr, f);
31302         }else{
31303             this.root.select();
31304             if(callback){
31305                 callback(true, this.root);
31306             }
31307         }
31308     },
31309
31310     getTreeEl : function(){
31311         return this.el;
31312     },
31313
31314     /**
31315      * Trigger rendering of this TreePanel
31316      */
31317     render : function(){
31318         if (this.innerCt) {
31319             return this; // stop it rendering more than once!!
31320         }
31321         
31322         this.innerCt = this.el.createChild({tag:"ul",
31323                cls:"x-tree-root-ct " +
31324                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31325
31326         if(this.containerScroll){
31327             Roo.dd.ScrollManager.register(this.el);
31328         }
31329         if((this.enableDD || this.enableDrop) && !this.dropZone){
31330            /**
31331             * The dropZone used by this tree if drop is enabled
31332             * @type Roo.tree.TreeDropZone
31333             */
31334              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31335                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31336            });
31337         }
31338         if((this.enableDD || this.enableDrag) && !this.dragZone){
31339            /**
31340             * The dragZone used by this tree if drag is enabled
31341             * @type Roo.tree.TreeDragZone
31342             */
31343             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31344                ddGroup: this.ddGroup || "TreeDD",
31345                scroll: this.ddScroll
31346            });
31347         }
31348         this.getSelectionModel().init(this);
31349         if (!this.root) {
31350             Roo.log("ROOT not set in tree");
31351             return this;
31352         }
31353         this.root.render();
31354         if(!this.rootVisible){
31355             this.root.renderChildren();
31356         }
31357         return this;
31358     }
31359 });/*
31360  * Based on:
31361  * Ext JS Library 1.1.1
31362  * Copyright(c) 2006-2007, Ext JS, LLC.
31363  *
31364  * Originally Released Under LGPL - original licence link has changed is not relivant.
31365  *
31366  * Fork - LGPL
31367  * <script type="text/javascript">
31368  */
31369  
31370
31371 /**
31372  * @class Roo.tree.DefaultSelectionModel
31373  * @extends Roo.util.Observable
31374  * The default single selection for a TreePanel.
31375  * @param {Object} cfg Configuration
31376  */
31377 Roo.tree.DefaultSelectionModel = function(cfg){
31378    this.selNode = null;
31379    
31380    
31381    
31382    this.addEvents({
31383        /**
31384         * @event selectionchange
31385         * Fires when the selected node changes
31386         * @param {DefaultSelectionModel} this
31387         * @param {TreeNode} node the new selection
31388         */
31389        "selectionchange" : true,
31390
31391        /**
31392         * @event beforeselect
31393         * Fires before the selected node changes, return false to cancel the change
31394         * @param {DefaultSelectionModel} this
31395         * @param {TreeNode} node the new selection
31396         * @param {TreeNode} node the old selection
31397         */
31398        "beforeselect" : true
31399    });
31400    
31401     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31402 };
31403
31404 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31405     init : function(tree){
31406         this.tree = tree;
31407         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31408         tree.on("click", this.onNodeClick, this);
31409     },
31410     
31411     onNodeClick : function(node, e){
31412         if (e.ctrlKey && this.selNode == node)  {
31413             this.unselect(node);
31414             return;
31415         }
31416         this.select(node);
31417     },
31418     
31419     /**
31420      * Select a node.
31421      * @param {TreeNode} node The node to select
31422      * @return {TreeNode} The selected node
31423      */
31424     select : function(node){
31425         var last = this.selNode;
31426         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31427             if(last){
31428                 last.ui.onSelectedChange(false);
31429             }
31430             this.selNode = node;
31431             node.ui.onSelectedChange(true);
31432             this.fireEvent("selectionchange", this, node, last);
31433         }
31434         return node;
31435     },
31436     
31437     /**
31438      * Deselect a node.
31439      * @param {TreeNode} node The node to unselect
31440      */
31441     unselect : function(node){
31442         if(this.selNode == node){
31443             this.clearSelections();
31444         }    
31445     },
31446     
31447     /**
31448      * Clear all selections
31449      */
31450     clearSelections : function(){
31451         var n = this.selNode;
31452         if(n){
31453             n.ui.onSelectedChange(false);
31454             this.selNode = null;
31455             this.fireEvent("selectionchange", this, null);
31456         }
31457         return n;
31458     },
31459     
31460     /**
31461      * Get the selected node
31462      * @return {TreeNode} The selected node
31463      */
31464     getSelectedNode : function(){
31465         return this.selNode;    
31466     },
31467     
31468     /**
31469      * Returns true if the node is selected
31470      * @param {TreeNode} node The node to check
31471      * @return {Boolean}
31472      */
31473     isSelected : function(node){
31474         return this.selNode == node;  
31475     },
31476
31477     /**
31478      * Selects the node above the selected node in the tree, intelligently walking the nodes
31479      * @return TreeNode The new selection
31480      */
31481     selectPrevious : function(){
31482         var s = this.selNode || this.lastSelNode;
31483         if(!s){
31484             return null;
31485         }
31486         var ps = s.previousSibling;
31487         if(ps){
31488             if(!ps.isExpanded() || ps.childNodes.length < 1){
31489                 return this.select(ps);
31490             } else{
31491                 var lc = ps.lastChild;
31492                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31493                     lc = lc.lastChild;
31494                 }
31495                 return this.select(lc);
31496             }
31497         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31498             return this.select(s.parentNode);
31499         }
31500         return null;
31501     },
31502
31503     /**
31504      * Selects the node above the selected node in the tree, intelligently walking the nodes
31505      * @return TreeNode The new selection
31506      */
31507     selectNext : function(){
31508         var s = this.selNode || this.lastSelNode;
31509         if(!s){
31510             return null;
31511         }
31512         if(s.firstChild && s.isExpanded()){
31513              return this.select(s.firstChild);
31514          }else if(s.nextSibling){
31515              return this.select(s.nextSibling);
31516          }else if(s.parentNode){
31517             var newS = null;
31518             s.parentNode.bubble(function(){
31519                 if(this.nextSibling){
31520                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31521                     return false;
31522                 }
31523             });
31524             return newS;
31525          }
31526         return null;
31527     },
31528
31529     onKeyDown : function(e){
31530         var s = this.selNode || this.lastSelNode;
31531         // undesirable, but required
31532         var sm = this;
31533         if(!s){
31534             return;
31535         }
31536         var k = e.getKey();
31537         switch(k){
31538              case e.DOWN:
31539                  e.stopEvent();
31540                  this.selectNext();
31541              break;
31542              case e.UP:
31543                  e.stopEvent();
31544                  this.selectPrevious();
31545              break;
31546              case e.RIGHT:
31547                  e.preventDefault();
31548                  if(s.hasChildNodes()){
31549                      if(!s.isExpanded()){
31550                          s.expand();
31551                      }else if(s.firstChild){
31552                          this.select(s.firstChild, e);
31553                      }
31554                  }
31555              break;
31556              case e.LEFT:
31557                  e.preventDefault();
31558                  if(s.hasChildNodes() && s.isExpanded()){
31559                      s.collapse();
31560                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31561                      this.select(s.parentNode, e);
31562                  }
31563              break;
31564         };
31565     }
31566 });
31567
31568 /**
31569  * @class Roo.tree.MultiSelectionModel
31570  * @extends Roo.util.Observable
31571  * Multi selection for a TreePanel.
31572  * @param {Object} cfg Configuration
31573  */
31574 Roo.tree.MultiSelectionModel = function(){
31575    this.selNodes = [];
31576    this.selMap = {};
31577    this.addEvents({
31578        /**
31579         * @event selectionchange
31580         * Fires when the selected nodes change
31581         * @param {MultiSelectionModel} this
31582         * @param {Array} nodes Array of the selected nodes
31583         */
31584        "selectionchange" : true
31585    });
31586    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31587    
31588 };
31589
31590 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31591     init : function(tree){
31592         this.tree = tree;
31593         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31594         tree.on("click", this.onNodeClick, this);
31595     },
31596     
31597     onNodeClick : function(node, e){
31598         this.select(node, e, e.ctrlKey);
31599     },
31600     
31601     /**
31602      * Select a node.
31603      * @param {TreeNode} node The node to select
31604      * @param {EventObject} e (optional) An event associated with the selection
31605      * @param {Boolean} keepExisting True to retain existing selections
31606      * @return {TreeNode} The selected node
31607      */
31608     select : function(node, e, keepExisting){
31609         if(keepExisting !== true){
31610             this.clearSelections(true);
31611         }
31612         if(this.isSelected(node)){
31613             this.lastSelNode = node;
31614             return node;
31615         }
31616         this.selNodes.push(node);
31617         this.selMap[node.id] = node;
31618         this.lastSelNode = node;
31619         node.ui.onSelectedChange(true);
31620         this.fireEvent("selectionchange", this, this.selNodes);
31621         return node;
31622     },
31623     
31624     /**
31625      * Deselect a node.
31626      * @param {TreeNode} node The node to unselect
31627      */
31628     unselect : function(node){
31629         if(this.selMap[node.id]){
31630             node.ui.onSelectedChange(false);
31631             var sn = this.selNodes;
31632             var index = -1;
31633             if(sn.indexOf){
31634                 index = sn.indexOf(node);
31635             }else{
31636                 for(var i = 0, len = sn.length; i < len; i++){
31637                     if(sn[i] == node){
31638                         index = i;
31639                         break;
31640                     }
31641                 }
31642             }
31643             if(index != -1){
31644                 this.selNodes.splice(index, 1);
31645             }
31646             delete this.selMap[node.id];
31647             this.fireEvent("selectionchange", this, this.selNodes);
31648         }
31649     },
31650     
31651     /**
31652      * Clear all selections
31653      */
31654     clearSelections : function(suppressEvent){
31655         var sn = this.selNodes;
31656         if(sn.length > 0){
31657             for(var i = 0, len = sn.length; i < len; i++){
31658                 sn[i].ui.onSelectedChange(false);
31659             }
31660             this.selNodes = [];
31661             this.selMap = {};
31662             if(suppressEvent !== true){
31663                 this.fireEvent("selectionchange", this, this.selNodes);
31664             }
31665         }
31666     },
31667     
31668     /**
31669      * Returns true if the node is selected
31670      * @param {TreeNode} node The node to check
31671      * @return {Boolean}
31672      */
31673     isSelected : function(node){
31674         return this.selMap[node.id] ? true : false;  
31675     },
31676     
31677     /**
31678      * Returns an array of the selected nodes
31679      * @return {Array}
31680      */
31681     getSelectedNodes : function(){
31682         return this.selNodes;    
31683     },
31684
31685     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31686
31687     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31688
31689     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31690 });/*
31691  * Based on:
31692  * Ext JS Library 1.1.1
31693  * Copyright(c) 2006-2007, Ext JS, LLC.
31694  *
31695  * Originally Released Under LGPL - original licence link has changed is not relivant.
31696  *
31697  * Fork - LGPL
31698  * <script type="text/javascript">
31699  */
31700  
31701 /**
31702  * @class Roo.tree.TreeNode
31703  * @extends Roo.data.Node
31704  * @cfg {String} text The text for this node
31705  * @cfg {Boolean} expanded true to start the node expanded
31706  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31707  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31708  * @cfg {Boolean} disabled true to start the node disabled
31709  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31710  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31711  * @cfg {String} cls A css class to be added to the node
31712  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31713  * @cfg {String} href URL of the link used for the node (defaults to #)
31714  * @cfg {String} hrefTarget target frame for the link
31715  * @cfg {String} qtip An Ext QuickTip for the node
31716  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31717  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31718  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31719  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31720  * (defaults to undefined with no checkbox rendered)
31721  * @constructor
31722  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31723  */
31724 Roo.tree.TreeNode = function(attributes){
31725     attributes = attributes || {};
31726     if(typeof attributes == "string"){
31727         attributes = {text: attributes};
31728     }
31729     this.childrenRendered = false;
31730     this.rendered = false;
31731     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31732     this.expanded = attributes.expanded === true;
31733     this.isTarget = attributes.isTarget !== false;
31734     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31735     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31736
31737     /**
31738      * Read-only. The text for this node. To change it use setText().
31739      * @type String
31740      */
31741     this.text = attributes.text;
31742     /**
31743      * True if this node is disabled.
31744      * @type Boolean
31745      */
31746     this.disabled = attributes.disabled === true;
31747
31748     this.addEvents({
31749         /**
31750         * @event textchange
31751         * Fires when the text for this node is changed
31752         * @param {Node} this This node
31753         * @param {String} text The new text
31754         * @param {String} oldText The old text
31755         */
31756         "textchange" : true,
31757         /**
31758         * @event beforeexpand
31759         * Fires before this node is expanded, return false to cancel.
31760         * @param {Node} this This node
31761         * @param {Boolean} deep
31762         * @param {Boolean} anim
31763         */
31764         "beforeexpand" : true,
31765         /**
31766         * @event beforecollapse
31767         * Fires before this node is collapsed, return false to cancel.
31768         * @param {Node} this This node
31769         * @param {Boolean} deep
31770         * @param {Boolean} anim
31771         */
31772         "beforecollapse" : true,
31773         /**
31774         * @event expand
31775         * Fires when this node is expanded
31776         * @param {Node} this This node
31777         */
31778         "expand" : true,
31779         /**
31780         * @event disabledchange
31781         * Fires when the disabled status of this node changes
31782         * @param {Node} this This node
31783         * @param {Boolean} disabled
31784         */
31785         "disabledchange" : true,
31786         /**
31787         * @event collapse
31788         * Fires when this node is collapsed
31789         * @param {Node} this This node
31790         */
31791         "collapse" : true,
31792         /**
31793         * @event beforeclick
31794         * Fires before click processing. Return false to cancel the default action.
31795         * @param {Node} this This node
31796         * @param {Roo.EventObject} e The event object
31797         */
31798         "beforeclick":true,
31799         /**
31800         * @event checkchange
31801         * Fires when a node with a checkbox's checked property changes
31802         * @param {Node} this This node
31803         * @param {Boolean} checked
31804         */
31805         "checkchange":true,
31806         /**
31807         * @event click
31808         * Fires when this node is clicked
31809         * @param {Node} this This node
31810         * @param {Roo.EventObject} e The event object
31811         */
31812         "click":true,
31813         /**
31814         * @event dblclick
31815         * Fires when this node is double clicked
31816         * @param {Node} this This node
31817         * @param {Roo.EventObject} e The event object
31818         */
31819         "dblclick":true,
31820         /**
31821         * @event contextmenu
31822         * Fires when this node is right clicked
31823         * @param {Node} this This node
31824         * @param {Roo.EventObject} e The event object
31825         */
31826         "contextmenu":true,
31827         /**
31828         * @event beforechildrenrendered
31829         * Fires right before the child nodes for this node are rendered
31830         * @param {Node} this This node
31831         */
31832         "beforechildrenrendered":true
31833     });
31834
31835     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31836
31837     /**
31838      * Read-only. The UI for this node
31839      * @type TreeNodeUI
31840      */
31841     this.ui = new uiClass(this);
31842     
31843     // finally support items[]
31844     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
31845         return;
31846     }
31847     
31848     
31849     Roo.each(this.attributes.items, function(c) {
31850         this.appendChild(Roo.factory(c,Roo.Tree));
31851     }, this);
31852     delete this.attributes.items;
31853     
31854     
31855     
31856 };
31857 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31858     preventHScroll: true,
31859     /**
31860      * Returns true if this node is expanded
31861      * @return {Boolean}
31862      */
31863     isExpanded : function(){
31864         return this.expanded;
31865     },
31866
31867     /**
31868      * Returns the UI object for this node
31869      * @return {TreeNodeUI}
31870      */
31871     getUI : function(){
31872         return this.ui;
31873     },
31874
31875     // private override
31876     setFirstChild : function(node){
31877         var of = this.firstChild;
31878         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31879         if(this.childrenRendered && of && node != of){
31880             of.renderIndent(true, true);
31881         }
31882         if(this.rendered){
31883             this.renderIndent(true, true);
31884         }
31885     },
31886
31887     // private override
31888     setLastChild : function(node){
31889         var ol = this.lastChild;
31890         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31891         if(this.childrenRendered && ol && node != ol){
31892             ol.renderIndent(true, true);
31893         }
31894         if(this.rendered){
31895             this.renderIndent(true, true);
31896         }
31897     },
31898
31899     // these methods are overridden to provide lazy rendering support
31900     // private override
31901     appendChild : function()
31902     {
31903         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31904         if(node && this.childrenRendered){
31905             node.render();
31906         }
31907         this.ui.updateExpandIcon();
31908         return node;
31909     },
31910
31911     // private override
31912     removeChild : function(node){
31913         this.ownerTree.getSelectionModel().unselect(node);
31914         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31915         // if it's been rendered remove dom node
31916         if(this.childrenRendered){
31917             node.ui.remove();
31918         }
31919         if(this.childNodes.length < 1){
31920             this.collapse(false, false);
31921         }else{
31922             this.ui.updateExpandIcon();
31923         }
31924         if(!this.firstChild) {
31925             this.childrenRendered = false;
31926         }
31927         return node;
31928     },
31929
31930     // private override
31931     insertBefore : function(node, refNode){
31932         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31933         if(newNode && refNode && this.childrenRendered){
31934             node.render();
31935         }
31936         this.ui.updateExpandIcon();
31937         return newNode;
31938     },
31939
31940     /**
31941      * Sets the text for this node
31942      * @param {String} text
31943      */
31944     setText : function(text){
31945         var oldText = this.text;
31946         this.text = text;
31947         this.attributes.text = text;
31948         if(this.rendered){ // event without subscribing
31949             this.ui.onTextChange(this, text, oldText);
31950         }
31951         this.fireEvent("textchange", this, text, oldText);
31952     },
31953
31954     /**
31955      * Triggers selection of this node
31956      */
31957     select : function(){
31958         this.getOwnerTree().getSelectionModel().select(this);
31959     },
31960
31961     /**
31962      * Triggers deselection of this node
31963      */
31964     unselect : function(){
31965         this.getOwnerTree().getSelectionModel().unselect(this);
31966     },
31967
31968     /**
31969      * Returns true if this node is selected
31970      * @return {Boolean}
31971      */
31972     isSelected : function(){
31973         return this.getOwnerTree().getSelectionModel().isSelected(this);
31974     },
31975
31976     /**
31977      * Expand this node.
31978      * @param {Boolean} deep (optional) True to expand all children as well
31979      * @param {Boolean} anim (optional) false to cancel the default animation
31980      * @param {Function} callback (optional) A callback to be called when
31981      * expanding this node completes (does not wait for deep expand to complete).
31982      * Called with 1 parameter, this node.
31983      */
31984     expand : function(deep, anim, callback){
31985         if(!this.expanded){
31986             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31987                 return;
31988             }
31989             if(!this.childrenRendered){
31990                 this.renderChildren();
31991             }
31992             this.expanded = true;
31993             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31994                 this.ui.animExpand(function(){
31995                     this.fireEvent("expand", this);
31996                     if(typeof callback == "function"){
31997                         callback(this);
31998                     }
31999                     if(deep === true){
32000                         this.expandChildNodes(true);
32001                     }
32002                 }.createDelegate(this));
32003                 return;
32004             }else{
32005                 this.ui.expand();
32006                 this.fireEvent("expand", this);
32007                 if(typeof callback == "function"){
32008                     callback(this);
32009                 }
32010             }
32011         }else{
32012            if(typeof callback == "function"){
32013                callback(this);
32014            }
32015         }
32016         if(deep === true){
32017             this.expandChildNodes(true);
32018         }
32019     },
32020
32021     isHiddenRoot : function(){
32022         return this.isRoot && !this.getOwnerTree().rootVisible;
32023     },
32024
32025     /**
32026      * Collapse this node.
32027      * @param {Boolean} deep (optional) True to collapse all children as well
32028      * @param {Boolean} anim (optional) false to cancel the default animation
32029      */
32030     collapse : function(deep, anim){
32031         if(this.expanded && !this.isHiddenRoot()){
32032             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32033                 return;
32034             }
32035             this.expanded = false;
32036             if((this.getOwnerTree().animate && anim !== false) || anim){
32037                 this.ui.animCollapse(function(){
32038                     this.fireEvent("collapse", this);
32039                     if(deep === true){
32040                         this.collapseChildNodes(true);
32041                     }
32042                 }.createDelegate(this));
32043                 return;
32044             }else{
32045                 this.ui.collapse();
32046                 this.fireEvent("collapse", this);
32047             }
32048         }
32049         if(deep === true){
32050             var cs = this.childNodes;
32051             for(var i = 0, len = cs.length; i < len; i++) {
32052                 cs[i].collapse(true, false);
32053             }
32054         }
32055     },
32056
32057     // private
32058     delayedExpand : function(delay){
32059         if(!this.expandProcId){
32060             this.expandProcId = this.expand.defer(delay, this);
32061         }
32062     },
32063
32064     // private
32065     cancelExpand : function(){
32066         if(this.expandProcId){
32067             clearTimeout(this.expandProcId);
32068         }
32069         this.expandProcId = false;
32070     },
32071
32072     /**
32073      * Toggles expanded/collapsed state of the node
32074      */
32075     toggle : function(){
32076         if(this.expanded){
32077             this.collapse();
32078         }else{
32079             this.expand();
32080         }
32081     },
32082
32083     /**
32084      * Ensures all parent nodes are expanded
32085      */
32086     ensureVisible : function(callback){
32087         var tree = this.getOwnerTree();
32088         tree.expandPath(this.parentNode.getPath(), false, function(){
32089             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32090             Roo.callback(callback);
32091         }.createDelegate(this));
32092     },
32093
32094     /**
32095      * Expand all child nodes
32096      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32097      */
32098     expandChildNodes : function(deep){
32099         var cs = this.childNodes;
32100         for(var i = 0, len = cs.length; i < len; i++) {
32101                 cs[i].expand(deep);
32102         }
32103     },
32104
32105     /**
32106      * Collapse all child nodes
32107      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32108      */
32109     collapseChildNodes : function(deep){
32110         var cs = this.childNodes;
32111         for(var i = 0, len = cs.length; i < len; i++) {
32112                 cs[i].collapse(deep);
32113         }
32114     },
32115
32116     /**
32117      * Disables this node
32118      */
32119     disable : function(){
32120         this.disabled = true;
32121         this.unselect();
32122         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32123             this.ui.onDisableChange(this, true);
32124         }
32125         this.fireEvent("disabledchange", this, true);
32126     },
32127
32128     /**
32129      * Enables this node
32130      */
32131     enable : function(){
32132         this.disabled = false;
32133         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32134             this.ui.onDisableChange(this, false);
32135         }
32136         this.fireEvent("disabledchange", this, false);
32137     },
32138
32139     // private
32140     renderChildren : function(suppressEvent){
32141         if(suppressEvent !== false){
32142             this.fireEvent("beforechildrenrendered", this);
32143         }
32144         var cs = this.childNodes;
32145         for(var i = 0, len = cs.length; i < len; i++){
32146             cs[i].render(true);
32147         }
32148         this.childrenRendered = true;
32149     },
32150
32151     // private
32152     sort : function(fn, scope){
32153         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32154         if(this.childrenRendered){
32155             var cs = this.childNodes;
32156             for(var i = 0, len = cs.length; i < len; i++){
32157                 cs[i].render(true);
32158             }
32159         }
32160     },
32161
32162     // private
32163     render : function(bulkRender){
32164         this.ui.render(bulkRender);
32165         if(!this.rendered){
32166             this.rendered = true;
32167             if(this.expanded){
32168                 this.expanded = false;
32169                 this.expand(false, false);
32170             }
32171         }
32172     },
32173
32174     // private
32175     renderIndent : function(deep, refresh){
32176         if(refresh){
32177             this.ui.childIndent = null;
32178         }
32179         this.ui.renderIndent();
32180         if(deep === true && this.childrenRendered){
32181             var cs = this.childNodes;
32182             for(var i = 0, len = cs.length; i < len; i++){
32183                 cs[i].renderIndent(true, refresh);
32184             }
32185         }
32186     }
32187 });/*
32188  * Based on:
32189  * Ext JS Library 1.1.1
32190  * Copyright(c) 2006-2007, Ext JS, LLC.
32191  *
32192  * Originally Released Under LGPL - original licence link has changed is not relivant.
32193  *
32194  * Fork - LGPL
32195  * <script type="text/javascript">
32196  */
32197  
32198 /**
32199  * @class Roo.tree.AsyncTreeNode
32200  * @extends Roo.tree.TreeNode
32201  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32202  * @constructor
32203  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32204  */
32205  Roo.tree.AsyncTreeNode = function(config){
32206     this.loaded = false;
32207     this.loading = false;
32208     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32209     /**
32210     * @event beforeload
32211     * Fires before this node is loaded, return false to cancel
32212     * @param {Node} this This node
32213     */
32214     this.addEvents({'beforeload':true, 'load': true});
32215     /**
32216     * @event load
32217     * Fires when this node is loaded
32218     * @param {Node} this This node
32219     */
32220     /**
32221      * The loader used by this node (defaults to using the tree's defined loader)
32222      * @type TreeLoader
32223      * @property loader
32224      */
32225 };
32226 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32227     expand : function(deep, anim, callback){
32228         if(this.loading){ // if an async load is already running, waiting til it's done
32229             var timer;
32230             var f = function(){
32231                 if(!this.loading){ // done loading
32232                     clearInterval(timer);
32233                     this.expand(deep, anim, callback);
32234                 }
32235             }.createDelegate(this);
32236             timer = setInterval(f, 200);
32237             return;
32238         }
32239         if(!this.loaded){
32240             if(this.fireEvent("beforeload", this) === false){
32241                 return;
32242             }
32243             this.loading = true;
32244             this.ui.beforeLoad(this);
32245             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32246             if(loader){
32247                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32248                 return;
32249             }
32250         }
32251         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32252     },
32253     
32254     /**
32255      * Returns true if this node is currently loading
32256      * @return {Boolean}
32257      */
32258     isLoading : function(){
32259         return this.loading;  
32260     },
32261     
32262     loadComplete : function(deep, anim, callback){
32263         this.loading = false;
32264         this.loaded = true;
32265         this.ui.afterLoad(this);
32266         this.fireEvent("load", this);
32267         this.expand(deep, anim, callback);
32268     },
32269     
32270     /**
32271      * Returns true if this node has been loaded
32272      * @return {Boolean}
32273      */
32274     isLoaded : function(){
32275         return this.loaded;
32276     },
32277     
32278     hasChildNodes : function(){
32279         if(!this.isLeaf() && !this.loaded){
32280             return true;
32281         }else{
32282             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32283         }
32284     },
32285
32286     /**
32287      * Trigger a reload for this node
32288      * @param {Function} callback
32289      */
32290     reload : function(callback){
32291         this.collapse(false, false);
32292         while(this.firstChild){
32293             this.removeChild(this.firstChild);
32294         }
32295         this.childrenRendered = false;
32296         this.loaded = false;
32297         if(this.isHiddenRoot()){
32298             this.expanded = false;
32299         }
32300         this.expand(false, false, callback);
32301     }
32302 });/*
32303  * Based on:
32304  * Ext JS Library 1.1.1
32305  * Copyright(c) 2006-2007, Ext JS, LLC.
32306  *
32307  * Originally Released Under LGPL - original licence link has changed is not relivant.
32308  *
32309  * Fork - LGPL
32310  * <script type="text/javascript">
32311  */
32312  
32313 /**
32314  * @class Roo.tree.TreeNodeUI
32315  * @constructor
32316  * @param {Object} node The node to render
32317  * The TreeNode UI implementation is separate from the
32318  * tree implementation. Unless you are customizing the tree UI,
32319  * you should never have to use this directly.
32320  */
32321 Roo.tree.TreeNodeUI = function(node){
32322     this.node = node;
32323     this.rendered = false;
32324     this.animating = false;
32325     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32326 };
32327
32328 Roo.tree.TreeNodeUI.prototype = {
32329     removeChild : function(node){
32330         if(this.rendered){
32331             this.ctNode.removeChild(node.ui.getEl());
32332         }
32333     },
32334
32335     beforeLoad : function(){
32336          this.addClass("x-tree-node-loading");
32337     },
32338
32339     afterLoad : function(){
32340          this.removeClass("x-tree-node-loading");
32341     },
32342
32343     onTextChange : function(node, text, oldText){
32344         if(this.rendered){
32345             this.textNode.innerHTML = text;
32346         }
32347     },
32348
32349     onDisableChange : function(node, state){
32350         this.disabled = state;
32351         if(state){
32352             this.addClass("x-tree-node-disabled");
32353         }else{
32354             this.removeClass("x-tree-node-disabled");
32355         }
32356     },
32357
32358     onSelectedChange : function(state){
32359         if(state){
32360             this.focus();
32361             this.addClass("x-tree-selected");
32362         }else{
32363             //this.blur();
32364             this.removeClass("x-tree-selected");
32365         }
32366     },
32367
32368     onMove : function(tree, node, oldParent, newParent, index, refNode){
32369         this.childIndent = null;
32370         if(this.rendered){
32371             var targetNode = newParent.ui.getContainer();
32372             if(!targetNode){//target not rendered
32373                 this.holder = document.createElement("div");
32374                 this.holder.appendChild(this.wrap);
32375                 return;
32376             }
32377             var insertBefore = refNode ? refNode.ui.getEl() : null;
32378             if(insertBefore){
32379                 targetNode.insertBefore(this.wrap, insertBefore);
32380             }else{
32381                 targetNode.appendChild(this.wrap);
32382             }
32383             this.node.renderIndent(true);
32384         }
32385     },
32386
32387     addClass : function(cls){
32388         if(this.elNode){
32389             Roo.fly(this.elNode).addClass(cls);
32390         }
32391     },
32392
32393     removeClass : function(cls){
32394         if(this.elNode){
32395             Roo.fly(this.elNode).removeClass(cls);
32396         }
32397     },
32398
32399     remove : function(){
32400         if(this.rendered){
32401             this.holder = document.createElement("div");
32402             this.holder.appendChild(this.wrap);
32403         }
32404     },
32405
32406     fireEvent : function(){
32407         return this.node.fireEvent.apply(this.node, arguments);
32408     },
32409
32410     initEvents : function(){
32411         this.node.on("move", this.onMove, this);
32412         var E = Roo.EventManager;
32413         var a = this.anchor;
32414
32415         var el = Roo.fly(a, '_treeui');
32416
32417         if(Roo.isOpera){ // opera render bug ignores the CSS
32418             el.setStyle("text-decoration", "none");
32419         }
32420
32421         el.on("click", this.onClick, this);
32422         el.on("dblclick", this.onDblClick, this);
32423
32424         if(this.checkbox){
32425             Roo.EventManager.on(this.checkbox,
32426                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32427         }
32428
32429         el.on("contextmenu", this.onContextMenu, this);
32430
32431         var icon = Roo.fly(this.iconNode);
32432         icon.on("click", this.onClick, this);
32433         icon.on("dblclick", this.onDblClick, this);
32434         icon.on("contextmenu", this.onContextMenu, this);
32435         E.on(this.ecNode, "click", this.ecClick, this, true);
32436
32437         if(this.node.disabled){
32438             this.addClass("x-tree-node-disabled");
32439         }
32440         if(this.node.hidden){
32441             this.addClass("x-tree-node-disabled");
32442         }
32443         var ot = this.node.getOwnerTree();
32444         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32445         if(dd && (!this.node.isRoot || ot.rootVisible)){
32446             Roo.dd.Registry.register(this.elNode, {
32447                 node: this.node,
32448                 handles: this.getDDHandles(),
32449                 isHandle: false
32450             });
32451         }
32452     },
32453
32454     getDDHandles : function(){
32455         return [this.iconNode, this.textNode];
32456     },
32457
32458     hide : function(){
32459         if(this.rendered){
32460             this.wrap.style.display = "none";
32461         }
32462     },
32463
32464     show : function(){
32465         if(this.rendered){
32466             this.wrap.style.display = "";
32467         }
32468     },
32469
32470     onContextMenu : function(e){
32471         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32472             e.preventDefault();
32473             this.focus();
32474             this.fireEvent("contextmenu", this.node, e);
32475         }
32476     },
32477
32478     onClick : function(e){
32479         if(this.dropping){
32480             e.stopEvent();
32481             return;
32482         }
32483         if(this.fireEvent("beforeclick", this.node, e) !== false){
32484             if(!this.disabled && this.node.attributes.href){
32485                 this.fireEvent("click", this.node, e);
32486                 return;
32487             }
32488             e.preventDefault();
32489             if(this.disabled){
32490                 return;
32491             }
32492
32493             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32494                 this.node.toggle();
32495             }
32496
32497             this.fireEvent("click", this.node, e);
32498         }else{
32499             e.stopEvent();
32500         }
32501     },
32502
32503     onDblClick : function(e){
32504         e.preventDefault();
32505         if(this.disabled){
32506             return;
32507         }
32508         if(this.checkbox){
32509             this.toggleCheck();
32510         }
32511         if(!this.animating && this.node.hasChildNodes()){
32512             this.node.toggle();
32513         }
32514         this.fireEvent("dblclick", this.node, e);
32515     },
32516
32517     onCheckChange : function(){
32518         var checked = this.checkbox.checked;
32519         this.node.attributes.checked = checked;
32520         this.fireEvent('checkchange', this.node, checked);
32521     },
32522
32523     ecClick : function(e){
32524         if(!this.animating && this.node.hasChildNodes()){
32525             this.node.toggle();
32526         }
32527     },
32528
32529     startDrop : function(){
32530         this.dropping = true;
32531     },
32532
32533     // delayed drop so the click event doesn't get fired on a drop
32534     endDrop : function(){
32535        setTimeout(function(){
32536            this.dropping = false;
32537        }.createDelegate(this), 50);
32538     },
32539
32540     expand : function(){
32541         this.updateExpandIcon();
32542         this.ctNode.style.display = "";
32543     },
32544
32545     focus : function(){
32546         if(!this.node.preventHScroll){
32547             try{this.anchor.focus();
32548             }catch(e){}
32549         }else if(!Roo.isIE){
32550             try{
32551                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32552                 var l = noscroll.scrollLeft;
32553                 this.anchor.focus();
32554                 noscroll.scrollLeft = l;
32555             }catch(e){}
32556         }
32557     },
32558
32559     toggleCheck : function(value){
32560         var cb = this.checkbox;
32561         if(cb){
32562             cb.checked = (value === undefined ? !cb.checked : value);
32563         }
32564     },
32565
32566     blur : function(){
32567         try{
32568             this.anchor.blur();
32569         }catch(e){}
32570     },
32571
32572     animExpand : function(callback){
32573         var ct = Roo.get(this.ctNode);
32574         ct.stopFx();
32575         if(!this.node.hasChildNodes()){
32576             this.updateExpandIcon();
32577             this.ctNode.style.display = "";
32578             Roo.callback(callback);
32579             return;
32580         }
32581         this.animating = true;
32582         this.updateExpandIcon();
32583
32584         ct.slideIn('t', {
32585            callback : function(){
32586                this.animating = false;
32587                Roo.callback(callback);
32588             },
32589             scope: this,
32590             duration: this.node.ownerTree.duration || .25
32591         });
32592     },
32593
32594     highlight : function(){
32595         var tree = this.node.getOwnerTree();
32596         Roo.fly(this.wrap).highlight(
32597             tree.hlColor || "C3DAF9",
32598             {endColor: tree.hlBaseColor}
32599         );
32600     },
32601
32602     collapse : function(){
32603         this.updateExpandIcon();
32604         this.ctNode.style.display = "none";
32605     },
32606
32607     animCollapse : function(callback){
32608         var ct = Roo.get(this.ctNode);
32609         ct.enableDisplayMode('block');
32610         ct.stopFx();
32611
32612         this.animating = true;
32613         this.updateExpandIcon();
32614
32615         ct.slideOut('t', {
32616             callback : function(){
32617                this.animating = false;
32618                Roo.callback(callback);
32619             },
32620             scope: this,
32621             duration: this.node.ownerTree.duration || .25
32622         });
32623     },
32624
32625     getContainer : function(){
32626         return this.ctNode;
32627     },
32628
32629     getEl : function(){
32630         return this.wrap;
32631     },
32632
32633     appendDDGhost : function(ghostNode){
32634         ghostNode.appendChild(this.elNode.cloneNode(true));
32635     },
32636
32637     getDDRepairXY : function(){
32638         return Roo.lib.Dom.getXY(this.iconNode);
32639     },
32640
32641     onRender : function(){
32642         this.render();
32643     },
32644
32645     render : function(bulkRender){
32646         var n = this.node, a = n.attributes;
32647         var targetNode = n.parentNode ?
32648               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32649
32650         if(!this.rendered){
32651             this.rendered = true;
32652
32653             this.renderElements(n, a, targetNode, bulkRender);
32654
32655             if(a.qtip){
32656                if(this.textNode.setAttributeNS){
32657                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32658                    if(a.qtipTitle){
32659                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32660                    }
32661                }else{
32662                    this.textNode.setAttribute("ext:qtip", a.qtip);
32663                    if(a.qtipTitle){
32664                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32665                    }
32666                }
32667             }else if(a.qtipCfg){
32668                 a.qtipCfg.target = Roo.id(this.textNode);
32669                 Roo.QuickTips.register(a.qtipCfg);
32670             }
32671             this.initEvents();
32672             if(!this.node.expanded){
32673                 this.updateExpandIcon();
32674             }
32675         }else{
32676             if(bulkRender === true) {
32677                 targetNode.appendChild(this.wrap);
32678             }
32679         }
32680     },
32681
32682     renderElements : function(n, a, targetNode, bulkRender)
32683     {
32684         // add some indent caching, this helps performance when rendering a large tree
32685         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32686         var t = n.getOwnerTree();
32687         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32688         if (typeof(n.attributes.html) != 'undefined') {
32689             txt = n.attributes.html;
32690         }
32691         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32692         var cb = typeof a.checked == 'boolean';
32693         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32694         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32695             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32696             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32697             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32698             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32699             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32700              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32701                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32702             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32703             "</li>"];
32704
32705         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32706             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32707                                 n.nextSibling.ui.getEl(), buf.join(""));
32708         }else{
32709             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32710         }
32711
32712         this.elNode = this.wrap.childNodes[0];
32713         this.ctNode = this.wrap.childNodes[1];
32714         var cs = this.elNode.childNodes;
32715         this.indentNode = cs[0];
32716         this.ecNode = cs[1];
32717         this.iconNode = cs[2];
32718         var index = 3;
32719         if(cb){
32720             this.checkbox = cs[3];
32721             index++;
32722         }
32723         this.anchor = cs[index];
32724         this.textNode = cs[index].firstChild;
32725     },
32726
32727     getAnchor : function(){
32728         return this.anchor;
32729     },
32730
32731     getTextEl : function(){
32732         return this.textNode;
32733     },
32734
32735     getIconEl : function(){
32736         return this.iconNode;
32737     },
32738
32739     isChecked : function(){
32740         return this.checkbox ? this.checkbox.checked : false;
32741     },
32742
32743     updateExpandIcon : function(){
32744         if(this.rendered){
32745             var n = this.node, c1, c2;
32746             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32747             var hasChild = n.hasChildNodes();
32748             if(hasChild){
32749                 if(n.expanded){
32750                     cls += "-minus";
32751                     c1 = "x-tree-node-collapsed";
32752                     c2 = "x-tree-node-expanded";
32753                 }else{
32754                     cls += "-plus";
32755                     c1 = "x-tree-node-expanded";
32756                     c2 = "x-tree-node-collapsed";
32757                 }
32758                 if(this.wasLeaf){
32759                     this.removeClass("x-tree-node-leaf");
32760                     this.wasLeaf = false;
32761                 }
32762                 if(this.c1 != c1 || this.c2 != c2){
32763                     Roo.fly(this.elNode).replaceClass(c1, c2);
32764                     this.c1 = c1; this.c2 = c2;
32765                 }
32766             }else{
32767                 // this changes non-leafs into leafs if they have no children.
32768                 // it's not very rational behaviour..
32769                 
32770                 if(!this.wasLeaf && this.node.leaf){
32771                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32772                     delete this.c1;
32773                     delete this.c2;
32774                     this.wasLeaf = true;
32775                 }
32776             }
32777             var ecc = "x-tree-ec-icon "+cls;
32778             if(this.ecc != ecc){
32779                 this.ecNode.className = ecc;
32780                 this.ecc = ecc;
32781             }
32782         }
32783     },
32784
32785     getChildIndent : function(){
32786         if(!this.childIndent){
32787             var buf = [];
32788             var p = this.node;
32789             while(p){
32790                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32791                     if(!p.isLast()) {
32792                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32793                     } else {
32794                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32795                     }
32796                 }
32797                 p = p.parentNode;
32798             }
32799             this.childIndent = buf.join("");
32800         }
32801         return this.childIndent;
32802     },
32803
32804     renderIndent : function(){
32805         if(this.rendered){
32806             var indent = "";
32807             var p = this.node.parentNode;
32808             if(p){
32809                 indent = p.ui.getChildIndent();
32810             }
32811             if(this.indentMarkup != indent){ // don't rerender if not required
32812                 this.indentNode.innerHTML = indent;
32813                 this.indentMarkup = indent;
32814             }
32815             this.updateExpandIcon();
32816         }
32817     }
32818 };
32819
32820 Roo.tree.RootTreeNodeUI = function(){
32821     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32822 };
32823 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32824     render : function(){
32825         if(!this.rendered){
32826             var targetNode = this.node.ownerTree.innerCt.dom;
32827             this.node.expanded = true;
32828             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32829             this.wrap = this.ctNode = targetNode.firstChild;
32830         }
32831     },
32832     collapse : function(){
32833     },
32834     expand : function(){
32835     }
32836 });/*
32837  * Based on:
32838  * Ext JS Library 1.1.1
32839  * Copyright(c) 2006-2007, Ext JS, LLC.
32840  *
32841  * Originally Released Under LGPL - original licence link has changed is not relivant.
32842  *
32843  * Fork - LGPL
32844  * <script type="text/javascript">
32845  */
32846 /**
32847  * @class Roo.tree.TreeLoader
32848  * @extends Roo.util.Observable
32849  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32850  * nodes from a specified URL. The response must be a javascript Array definition
32851  * who's elements are node definition objects. eg:
32852  * <pre><code>
32853 {  success : true,
32854    data :      [
32855    
32856     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
32857     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
32858     ]
32859 }
32860
32861
32862 </code></pre>
32863  * <br><br>
32864  * The old style respose with just an array is still supported, but not recommended.
32865  * <br><br>
32866  *
32867  * A server request is sent, and child nodes are loaded only when a node is expanded.
32868  * The loading node's id is passed to the server under the parameter name "node" to
32869  * enable the server to produce the correct child nodes.
32870  * <br><br>
32871  * To pass extra parameters, an event handler may be attached to the "beforeload"
32872  * event, and the parameters specified in the TreeLoader's baseParams property:
32873  * <pre><code>
32874     myTreeLoader.on("beforeload", function(treeLoader, node) {
32875         this.baseParams.category = node.attributes.category;
32876     }, this);
32877 </code></pre><
32878  * This would pass an HTTP parameter called "category" to the server containing
32879  * the value of the Node's "category" attribute.
32880  * @constructor
32881  * Creates a new Treeloader.
32882  * @param {Object} config A config object containing config properties.
32883  */
32884 Roo.tree.TreeLoader = function(config){
32885     this.baseParams = {};
32886     this.requestMethod = "POST";
32887     Roo.apply(this, config);
32888
32889     this.addEvents({
32890     
32891         /**
32892          * @event beforeload
32893          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32894          * @param {Object} This TreeLoader object.
32895          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32896          * @param {Object} callback The callback function specified in the {@link #load} call.
32897          */
32898         beforeload : true,
32899         /**
32900          * @event load
32901          * Fires when the node has been successfuly loaded.
32902          * @param {Object} This TreeLoader object.
32903          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32904          * @param {Object} response The response object containing the data from the server.
32905          */
32906         load : true,
32907         /**
32908          * @event loadexception
32909          * Fires if the network request failed.
32910          * @param {Object} This TreeLoader object.
32911          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32912          * @param {Object} response The response object containing the data from the server.
32913          */
32914         loadexception : true,
32915         /**
32916          * @event create
32917          * Fires before a node is created, enabling you to return custom Node types 
32918          * @param {Object} This TreeLoader object.
32919          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32920          */
32921         create : true
32922     });
32923
32924     Roo.tree.TreeLoader.superclass.constructor.call(this);
32925 };
32926
32927 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32928     /**
32929     * @cfg {String} dataUrl The URL from which to request a Json string which
32930     * specifies an array of node definition object representing the child nodes
32931     * to be loaded.
32932     */
32933     /**
32934     * @cfg {Object} baseParams (optional) An object containing properties which
32935     * specify HTTP parameters to be passed to each request for child nodes.
32936     */
32937     /**
32938     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32939     * created by this loader. If the attributes sent by the server have an attribute in this object,
32940     * they take priority.
32941     */
32942     /**
32943     * @cfg {Object} uiProviders (optional) An object containing properties which
32944     * 
32945     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32946     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32947     * <i>uiProvider</i> attribute of a returned child node is a string rather
32948     * than a reference to a TreeNodeUI implementation, this that string value
32949     * is used as a property name in the uiProviders object. You can define the provider named
32950     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32951     */
32952     uiProviders : {},
32953
32954     /**
32955     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32956     * child nodes before loading.
32957     */
32958     clearOnLoad : true,
32959
32960     /**
32961     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32962     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32963     * Grid query { data : [ .....] }
32964     */
32965     
32966     root : false,
32967      /**
32968     * @cfg {String} queryParam (optional) 
32969     * Name of the query as it will be passed on the querystring (defaults to 'node')
32970     * eg. the request will be ?node=[id]
32971     */
32972     
32973     
32974     queryParam: false,
32975     
32976     /**
32977      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32978      * This is called automatically when a node is expanded, but may be used to reload
32979      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32980      * @param {Roo.tree.TreeNode} node
32981      * @param {Function} callback
32982      */
32983     load : function(node, callback){
32984         if(this.clearOnLoad){
32985             while(node.firstChild){
32986                 node.removeChild(node.firstChild);
32987             }
32988         }
32989         if(node.attributes.children){ // preloaded json children
32990             var cs = node.attributes.children;
32991             for(var i = 0, len = cs.length; i < len; i++){
32992                 node.appendChild(this.createNode(cs[i]));
32993             }
32994             if(typeof callback == "function"){
32995                 callback();
32996             }
32997         }else if(this.dataUrl){
32998             this.requestData(node, callback);
32999         }
33000     },
33001
33002     getParams: function(node){
33003         var buf = [], bp = this.baseParams;
33004         for(var key in bp){
33005             if(typeof bp[key] != "function"){
33006                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33007             }
33008         }
33009         var n = this.queryParam === false ? 'node' : this.queryParam;
33010         buf.push(n + "=", encodeURIComponent(node.id));
33011         return buf.join("");
33012     },
33013
33014     requestData : function(node, callback){
33015         if(this.fireEvent("beforeload", this, node, callback) !== false){
33016             this.transId = Roo.Ajax.request({
33017                 method:this.requestMethod,
33018                 url: this.dataUrl||this.url,
33019                 success: this.handleResponse,
33020                 failure: this.handleFailure,
33021                 scope: this,
33022                 argument: {callback: callback, node: node},
33023                 params: this.getParams(node)
33024             });
33025         }else{
33026             // if the load is cancelled, make sure we notify
33027             // the node that we are done
33028             if(typeof callback == "function"){
33029                 callback();
33030             }
33031         }
33032     },
33033
33034     isLoading : function(){
33035         return this.transId ? true : false;
33036     },
33037
33038     abort : function(){
33039         if(this.isLoading()){
33040             Roo.Ajax.abort(this.transId);
33041         }
33042     },
33043
33044     // private
33045     createNode : function(attr)
33046     {
33047         // apply baseAttrs, nice idea Corey!
33048         if(this.baseAttrs){
33049             Roo.applyIf(attr, this.baseAttrs);
33050         }
33051         if(this.applyLoader !== false){
33052             attr.loader = this;
33053         }
33054         // uiProvider = depreciated..
33055         
33056         if(typeof(attr.uiProvider) == 'string'){
33057            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33058                 /**  eval:var:attr */ eval(attr.uiProvider);
33059         }
33060         if(typeof(this.uiProviders['default']) != 'undefined') {
33061             attr.uiProvider = this.uiProviders['default'];
33062         }
33063         
33064         this.fireEvent('create', this, attr);
33065         
33066         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33067         return(attr.leaf ?
33068                         new Roo.tree.TreeNode(attr) :
33069                         new Roo.tree.AsyncTreeNode(attr));
33070     },
33071
33072     processResponse : function(response, node, callback)
33073     {
33074         var json = response.responseText;
33075         try {
33076             
33077             var o = Roo.decode(json);
33078             
33079             if (this.root === false && typeof(o.success) != undefined) {
33080                 this.root = 'data'; // the default behaviour for list like data..
33081                 }
33082                 
33083             if (this.root !== false &&  !o.success) {
33084                 // it's a failure condition.
33085                 var a = response.argument;
33086                 this.fireEvent("loadexception", this, a.node, response);
33087                 Roo.log("Load failed - should have a handler really");
33088                 return;
33089             }
33090             
33091             
33092             
33093             if (this.root !== false) {
33094                  o = o[this.root];
33095             }
33096             
33097             for(var i = 0, len = o.length; i < len; i++){
33098                 var n = this.createNode(o[i]);
33099                 if(n){
33100                     node.appendChild(n);
33101                 }
33102             }
33103             if(typeof callback == "function"){
33104                 callback(this, node);
33105             }
33106         }catch(e){
33107             this.handleFailure(response);
33108         }
33109     },
33110
33111     handleResponse : function(response){
33112         this.transId = false;
33113         var a = response.argument;
33114         this.processResponse(response, a.node, a.callback);
33115         this.fireEvent("load", this, a.node, response);
33116     },
33117
33118     handleFailure : function(response)
33119     {
33120         // should handle failure better..
33121         this.transId = false;
33122         var a = response.argument;
33123         this.fireEvent("loadexception", this, a.node, response);
33124         if(typeof a.callback == "function"){
33125             a.callback(this, a.node);
33126         }
33127     }
33128 });/*
33129  * Based on:
33130  * Ext JS Library 1.1.1
33131  * Copyright(c) 2006-2007, Ext JS, LLC.
33132  *
33133  * Originally Released Under LGPL - original licence link has changed is not relivant.
33134  *
33135  * Fork - LGPL
33136  * <script type="text/javascript">
33137  */
33138
33139 /**
33140 * @class Roo.tree.TreeFilter
33141 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33142 * @param {TreePanel} tree
33143 * @param {Object} config (optional)
33144  */
33145 Roo.tree.TreeFilter = function(tree, config){
33146     this.tree = tree;
33147     this.filtered = {};
33148     Roo.apply(this, config);
33149 };
33150
33151 Roo.tree.TreeFilter.prototype = {
33152     clearBlank:false,
33153     reverse:false,
33154     autoClear:false,
33155     remove:false,
33156
33157      /**
33158      * Filter the data by a specific attribute.
33159      * @param {String/RegExp} value Either string that the attribute value
33160      * should start with or a RegExp to test against the attribute
33161      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33162      * @param {TreeNode} startNode (optional) The node to start the filter at.
33163      */
33164     filter : function(value, attr, startNode){
33165         attr = attr || "text";
33166         var f;
33167         if(typeof value == "string"){
33168             var vlen = value.length;
33169             // auto clear empty filter
33170             if(vlen == 0 && this.clearBlank){
33171                 this.clear();
33172                 return;
33173             }
33174             value = value.toLowerCase();
33175             f = function(n){
33176                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33177             };
33178         }else if(value.exec){ // regex?
33179             f = function(n){
33180                 return value.test(n.attributes[attr]);
33181             };
33182         }else{
33183             throw 'Illegal filter type, must be string or regex';
33184         }
33185         this.filterBy(f, null, startNode);
33186         },
33187
33188     /**
33189      * Filter by a function. The passed function will be called with each
33190      * node in the tree (or from the startNode). If the function returns true, the node is kept
33191      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33192      * @param {Function} fn The filter function
33193      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33194      */
33195     filterBy : function(fn, scope, startNode){
33196         startNode = startNode || this.tree.root;
33197         if(this.autoClear){
33198             this.clear();
33199         }
33200         var af = this.filtered, rv = this.reverse;
33201         var f = function(n){
33202             if(n == startNode){
33203                 return true;
33204             }
33205             if(af[n.id]){
33206                 return false;
33207             }
33208             var m = fn.call(scope || n, n);
33209             if(!m || rv){
33210                 af[n.id] = n;
33211                 n.ui.hide();
33212                 return false;
33213             }
33214             return true;
33215         };
33216         startNode.cascade(f);
33217         if(this.remove){
33218            for(var id in af){
33219                if(typeof id != "function"){
33220                    var n = af[id];
33221                    if(n && n.parentNode){
33222                        n.parentNode.removeChild(n);
33223                    }
33224                }
33225            }
33226         }
33227     },
33228
33229     /**
33230      * Clears the current filter. Note: with the "remove" option
33231      * set a filter cannot be cleared.
33232      */
33233     clear : function(){
33234         var t = this.tree;
33235         var af = this.filtered;
33236         for(var id in af){
33237             if(typeof id != "function"){
33238                 var n = af[id];
33239                 if(n){
33240                     n.ui.show();
33241                 }
33242             }
33243         }
33244         this.filtered = {};
33245     }
33246 };
33247 /*
33248  * Based on:
33249  * Ext JS Library 1.1.1
33250  * Copyright(c) 2006-2007, Ext JS, LLC.
33251  *
33252  * Originally Released Under LGPL - original licence link has changed is not relivant.
33253  *
33254  * Fork - LGPL
33255  * <script type="text/javascript">
33256  */
33257  
33258
33259 /**
33260  * @class Roo.tree.TreeSorter
33261  * Provides sorting of nodes in a TreePanel
33262  * 
33263  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33264  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33265  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33266  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33267  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33268  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33269  * @constructor
33270  * @param {TreePanel} tree
33271  * @param {Object} config
33272  */
33273 Roo.tree.TreeSorter = function(tree, config){
33274     Roo.apply(this, config);
33275     tree.on("beforechildrenrendered", this.doSort, this);
33276     tree.on("append", this.updateSort, this);
33277     tree.on("insert", this.updateSort, this);
33278     
33279     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33280     var p = this.property || "text";
33281     var sortType = this.sortType;
33282     var fs = this.folderSort;
33283     var cs = this.caseSensitive === true;
33284     var leafAttr = this.leafAttr || 'leaf';
33285
33286     this.sortFn = function(n1, n2){
33287         if(fs){
33288             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33289                 return 1;
33290             }
33291             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33292                 return -1;
33293             }
33294         }
33295         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33296         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33297         if(v1 < v2){
33298                         return dsc ? +1 : -1;
33299                 }else if(v1 > v2){
33300                         return dsc ? -1 : +1;
33301         }else{
33302                 return 0;
33303         }
33304     };
33305 };
33306
33307 Roo.tree.TreeSorter.prototype = {
33308     doSort : function(node){
33309         node.sort(this.sortFn);
33310     },
33311     
33312     compareNodes : function(n1, n2){
33313         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33314     },
33315     
33316     updateSort : function(tree, node){
33317         if(node.childrenRendered){
33318             this.doSort.defer(1, this, [node]);
33319         }
33320     }
33321 };/*
33322  * Based on:
33323  * Ext JS Library 1.1.1
33324  * Copyright(c) 2006-2007, Ext JS, LLC.
33325  *
33326  * Originally Released Under LGPL - original licence link has changed is not relivant.
33327  *
33328  * Fork - LGPL
33329  * <script type="text/javascript">
33330  */
33331
33332 if(Roo.dd.DropZone){
33333     
33334 Roo.tree.TreeDropZone = function(tree, config){
33335     this.allowParentInsert = false;
33336     this.allowContainerDrop = false;
33337     this.appendOnly = false;
33338     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33339     this.tree = tree;
33340     this.lastInsertClass = "x-tree-no-status";
33341     this.dragOverData = {};
33342 };
33343
33344 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33345     ddGroup : "TreeDD",
33346     
33347     expandDelay : 1000,
33348     
33349     expandNode : function(node){
33350         if(node.hasChildNodes() && !node.isExpanded()){
33351             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33352         }
33353     },
33354     
33355     queueExpand : function(node){
33356         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33357     },
33358     
33359     cancelExpand : function(){
33360         if(this.expandProcId){
33361             clearTimeout(this.expandProcId);
33362             this.expandProcId = false;
33363         }
33364     },
33365     
33366     isValidDropPoint : function(n, pt, dd, e, data){
33367         if(!n || !data){ return false; }
33368         var targetNode = n.node;
33369         var dropNode = data.node;
33370         // default drop rules
33371         if(!(targetNode && targetNode.isTarget && pt)){
33372             return false;
33373         }
33374         if(pt == "append" && targetNode.allowChildren === false){
33375             return false;
33376         }
33377         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33378             return false;
33379         }
33380         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33381             return false;
33382         }
33383         // reuse the object
33384         var overEvent = this.dragOverData;
33385         overEvent.tree = this.tree;
33386         overEvent.target = targetNode;
33387         overEvent.data = data;
33388         overEvent.point = pt;
33389         overEvent.source = dd;
33390         overEvent.rawEvent = e;
33391         overEvent.dropNode = dropNode;
33392         overEvent.cancel = false;  
33393         var result = this.tree.fireEvent("nodedragover", overEvent);
33394         return overEvent.cancel === false && result !== false;
33395     },
33396     
33397     getDropPoint : function(e, n, dd){
33398         var tn = n.node;
33399         if(tn.isRoot){
33400             return tn.allowChildren !== false ? "append" : false; // always append for root
33401         }
33402         var dragEl = n.ddel;
33403         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33404         var y = Roo.lib.Event.getPageY(e);
33405         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33406         
33407         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33408         var noAppend = tn.allowChildren === false;
33409         if(this.appendOnly || tn.parentNode.allowChildren === false){
33410             return noAppend ? false : "append";
33411         }
33412         var noBelow = false;
33413         if(!this.allowParentInsert){
33414             noBelow = tn.hasChildNodes() && tn.isExpanded();
33415         }
33416         var q = (b - t) / (noAppend ? 2 : 3);
33417         if(y >= t && y < (t + q)){
33418             return "above";
33419         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33420             return "below";
33421         }else{
33422             return "append";
33423         }
33424     },
33425     
33426     onNodeEnter : function(n, dd, e, data){
33427         this.cancelExpand();
33428     },
33429     
33430     onNodeOver : function(n, dd, e, data){
33431         var pt = this.getDropPoint(e, n, dd);
33432         var node = n.node;
33433         
33434         // auto node expand check
33435         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33436             this.queueExpand(node);
33437         }else if(pt != "append"){
33438             this.cancelExpand();
33439         }
33440         
33441         // set the insert point style on the target node
33442         var returnCls = this.dropNotAllowed;
33443         if(this.isValidDropPoint(n, pt, dd, e, data)){
33444            if(pt){
33445                var el = n.ddel;
33446                var cls;
33447                if(pt == "above"){
33448                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33449                    cls = "x-tree-drag-insert-above";
33450                }else if(pt == "below"){
33451                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33452                    cls = "x-tree-drag-insert-below";
33453                }else{
33454                    returnCls = "x-tree-drop-ok-append";
33455                    cls = "x-tree-drag-append";
33456                }
33457                if(this.lastInsertClass != cls){
33458                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33459                    this.lastInsertClass = cls;
33460                }
33461            }
33462        }
33463        return returnCls;
33464     },
33465     
33466     onNodeOut : function(n, dd, e, data){
33467         this.cancelExpand();
33468         this.removeDropIndicators(n);
33469     },
33470     
33471     onNodeDrop : function(n, dd, e, data){
33472         var point = this.getDropPoint(e, n, dd);
33473         var targetNode = n.node;
33474         targetNode.ui.startDrop();
33475         if(!this.isValidDropPoint(n, point, dd, e, data)){
33476             targetNode.ui.endDrop();
33477             return false;
33478         }
33479         // first try to find the drop node
33480         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33481         var dropEvent = {
33482             tree : this.tree,
33483             target: targetNode,
33484             data: data,
33485             point: point,
33486             source: dd,
33487             rawEvent: e,
33488             dropNode: dropNode,
33489             cancel: !dropNode   
33490         };
33491         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33492         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33493             targetNode.ui.endDrop();
33494             return false;
33495         }
33496         // allow target changing
33497         targetNode = dropEvent.target;
33498         if(point == "append" && !targetNode.isExpanded()){
33499             targetNode.expand(false, null, function(){
33500                 this.completeDrop(dropEvent);
33501             }.createDelegate(this));
33502         }else{
33503             this.completeDrop(dropEvent);
33504         }
33505         return true;
33506     },
33507     
33508     completeDrop : function(de){
33509         var ns = de.dropNode, p = de.point, t = de.target;
33510         if(!(ns instanceof Array)){
33511             ns = [ns];
33512         }
33513         var n;
33514         for(var i = 0, len = ns.length; i < len; i++){
33515             n = ns[i];
33516             if(p == "above"){
33517                 t.parentNode.insertBefore(n, t);
33518             }else if(p == "below"){
33519                 t.parentNode.insertBefore(n, t.nextSibling);
33520             }else{
33521                 t.appendChild(n);
33522             }
33523         }
33524         n.ui.focus();
33525         if(this.tree.hlDrop){
33526             n.ui.highlight();
33527         }
33528         t.ui.endDrop();
33529         this.tree.fireEvent("nodedrop", de);
33530     },
33531     
33532     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33533         if(this.tree.hlDrop){
33534             dropNode.ui.focus();
33535             dropNode.ui.highlight();
33536         }
33537         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33538     },
33539     
33540     getTree : function(){
33541         return this.tree;
33542     },
33543     
33544     removeDropIndicators : function(n){
33545         if(n && n.ddel){
33546             var el = n.ddel;
33547             Roo.fly(el).removeClass([
33548                     "x-tree-drag-insert-above",
33549                     "x-tree-drag-insert-below",
33550                     "x-tree-drag-append"]);
33551             this.lastInsertClass = "_noclass";
33552         }
33553     },
33554     
33555     beforeDragDrop : function(target, e, id){
33556         this.cancelExpand();
33557         return true;
33558     },
33559     
33560     afterRepair : function(data){
33561         if(data && Roo.enableFx){
33562             data.node.ui.highlight();
33563         }
33564         this.hideProxy();
33565     }    
33566 });
33567
33568 }
33569 /*
33570  * Based on:
33571  * Ext JS Library 1.1.1
33572  * Copyright(c) 2006-2007, Ext JS, LLC.
33573  *
33574  * Originally Released Under LGPL - original licence link has changed is not relivant.
33575  *
33576  * Fork - LGPL
33577  * <script type="text/javascript">
33578  */
33579  
33580
33581 if(Roo.dd.DragZone){
33582 Roo.tree.TreeDragZone = function(tree, config){
33583     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33584     this.tree = tree;
33585 };
33586
33587 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33588     ddGroup : "TreeDD",
33589     
33590     onBeforeDrag : function(data, e){
33591         var n = data.node;
33592         return n && n.draggable && !n.disabled;
33593     },
33594     
33595     onInitDrag : function(e){
33596         var data = this.dragData;
33597         this.tree.getSelectionModel().select(data.node);
33598         this.proxy.update("");
33599         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33600         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33601     },
33602     
33603     getRepairXY : function(e, data){
33604         return data.node.ui.getDDRepairXY();
33605     },
33606     
33607     onEndDrag : function(data, e){
33608         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33609     },
33610     
33611     onValidDrop : function(dd, e, id){
33612         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33613         this.hideProxy();
33614     },
33615     
33616     beforeInvalidDrop : function(e, id){
33617         // this scrolls the original position back into view
33618         var sm = this.tree.getSelectionModel();
33619         sm.clearSelections();
33620         sm.select(this.dragData.node);
33621     }
33622 });
33623 }/*
33624  * Based on:
33625  * Ext JS Library 1.1.1
33626  * Copyright(c) 2006-2007, Ext JS, LLC.
33627  *
33628  * Originally Released Under LGPL - original licence link has changed is not relivant.
33629  *
33630  * Fork - LGPL
33631  * <script type="text/javascript">
33632  */
33633 /**
33634  * @class Roo.tree.TreeEditor
33635  * @extends Roo.Editor
33636  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33637  * as the editor field.
33638  * @constructor
33639  * @param {Object} config (used to be the tree panel.)
33640  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33641  * 
33642  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33643  * @cfg {Roo.form.TextField|Object} field The field configuration
33644  *
33645  * 
33646  */
33647 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33648     var tree = config;
33649     var field;
33650     if (oldconfig) { // old style..
33651         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33652     } else {
33653         // new style..
33654         tree = config.tree;
33655         config.field = config.field  || {};
33656         config.field.xtype = 'TextField';
33657         field = Roo.factory(config.field, Roo.form);
33658     }
33659     config = config || {};
33660     
33661     
33662     this.addEvents({
33663         /**
33664          * @event beforenodeedit
33665          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33666          * false from the handler of this event.
33667          * @param {Editor} this
33668          * @param {Roo.tree.Node} node 
33669          */
33670         "beforenodeedit" : true
33671     });
33672     
33673     //Roo.log(config);
33674     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33675
33676     this.tree = tree;
33677
33678     tree.on('beforeclick', this.beforeNodeClick, this);
33679     tree.getTreeEl().on('mousedown', this.hide, this);
33680     this.on('complete', this.updateNode, this);
33681     this.on('beforestartedit', this.fitToTree, this);
33682     this.on('startedit', this.bindScroll, this, {delay:10});
33683     this.on('specialkey', this.onSpecialKey, this);
33684 };
33685
33686 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33687     /**
33688      * @cfg {String} alignment
33689      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33690      */
33691     alignment: "l-l",
33692     // inherit
33693     autoSize: false,
33694     /**
33695      * @cfg {Boolean} hideEl
33696      * True to hide the bound element while the editor is displayed (defaults to false)
33697      */
33698     hideEl : false,
33699     /**
33700      * @cfg {String} cls
33701      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33702      */
33703     cls: "x-small-editor x-tree-editor",
33704     /**
33705      * @cfg {Boolean} shim
33706      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33707      */
33708     shim:false,
33709     // inherit
33710     shadow:"frame",
33711     /**
33712      * @cfg {Number} maxWidth
33713      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33714      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33715      * scroll and client offsets into account prior to each edit.
33716      */
33717     maxWidth: 250,
33718
33719     editDelay : 350,
33720
33721     // private
33722     fitToTree : function(ed, el){
33723         var td = this.tree.getTreeEl().dom, nd = el.dom;
33724         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33725             td.scrollLeft = nd.offsetLeft;
33726         }
33727         var w = Math.min(
33728                 this.maxWidth,
33729                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33730         this.setSize(w, '');
33731         
33732         return this.fireEvent('beforenodeedit', this, this.editNode);
33733         
33734     },
33735
33736     // private
33737     triggerEdit : function(node){
33738         this.completeEdit();
33739         this.editNode = node;
33740         this.startEdit(node.ui.textNode, node.text);
33741     },
33742
33743     // private
33744     bindScroll : function(){
33745         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33746     },
33747
33748     // private
33749     beforeNodeClick : function(node, e){
33750         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33751         this.lastClick = new Date();
33752         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33753             e.stopEvent();
33754             this.triggerEdit(node);
33755             return false;
33756         }
33757         return true;
33758     },
33759
33760     // private
33761     updateNode : function(ed, value){
33762         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33763         this.editNode.setText(value);
33764     },
33765
33766     // private
33767     onHide : function(){
33768         Roo.tree.TreeEditor.superclass.onHide.call(this);
33769         if(this.editNode){
33770             this.editNode.ui.focus();
33771         }
33772     },
33773
33774     // private
33775     onSpecialKey : function(field, e){
33776         var k = e.getKey();
33777         if(k == e.ESC){
33778             e.stopEvent();
33779             this.cancelEdit();
33780         }else if(k == e.ENTER && !e.hasModifier()){
33781             e.stopEvent();
33782             this.completeEdit();
33783         }
33784     }
33785 });//<Script type="text/javascript">
33786 /*
33787  * Based on:
33788  * Ext JS Library 1.1.1
33789  * Copyright(c) 2006-2007, Ext JS, LLC.
33790  *
33791  * Originally Released Under LGPL - original licence link has changed is not relivant.
33792  *
33793  * Fork - LGPL
33794  * <script type="text/javascript">
33795  */
33796  
33797 /**
33798  * Not documented??? - probably should be...
33799  */
33800
33801 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33802     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33803     
33804     renderElements : function(n, a, targetNode, bulkRender){
33805         //consel.log("renderElements?");
33806         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33807
33808         var t = n.getOwnerTree();
33809         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33810         
33811         var cols = t.columns;
33812         var bw = t.borderWidth;
33813         var c = cols[0];
33814         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33815          var cb = typeof a.checked == "boolean";
33816         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33817         var colcls = 'x-t-' + tid + '-c0';
33818         var buf = [
33819             '<li class="x-tree-node">',
33820             
33821                 
33822                 '<div class="x-tree-node-el ', a.cls,'">',
33823                     // extran...
33824                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33825                 
33826                 
33827                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33828                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33829                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33830                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33831                            (a.iconCls ? ' '+a.iconCls : ''),
33832                            '" unselectable="on" />',
33833                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33834                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33835                              
33836                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33837                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33838                             '<span unselectable="on" qtip="' + tx + '">',
33839                              tx,
33840                              '</span></a>' ,
33841                     '</div>',
33842                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33843                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33844                  ];
33845         for(var i = 1, len = cols.length; i < len; i++){
33846             c = cols[i];
33847             colcls = 'x-t-' + tid + '-c' +i;
33848             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33849             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33850                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33851                       "</div>");
33852          }
33853          
33854          buf.push(
33855             '</a>',
33856             '<div class="x-clear"></div></div>',
33857             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33858             "</li>");
33859         
33860         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33861             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33862                                 n.nextSibling.ui.getEl(), buf.join(""));
33863         }else{
33864             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33865         }
33866         var el = this.wrap.firstChild;
33867         this.elRow = el;
33868         this.elNode = el.firstChild;
33869         this.ranchor = el.childNodes[1];
33870         this.ctNode = this.wrap.childNodes[1];
33871         var cs = el.firstChild.childNodes;
33872         this.indentNode = cs[0];
33873         this.ecNode = cs[1];
33874         this.iconNode = cs[2];
33875         var index = 3;
33876         if(cb){
33877             this.checkbox = cs[3];
33878             index++;
33879         }
33880         this.anchor = cs[index];
33881         
33882         this.textNode = cs[index].firstChild;
33883         
33884         //el.on("click", this.onClick, this);
33885         //el.on("dblclick", this.onDblClick, this);
33886         
33887         
33888        // console.log(this);
33889     },
33890     initEvents : function(){
33891         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33892         
33893             
33894         var a = this.ranchor;
33895
33896         var el = Roo.get(a);
33897
33898         if(Roo.isOpera){ // opera render bug ignores the CSS
33899             el.setStyle("text-decoration", "none");
33900         }
33901
33902         el.on("click", this.onClick, this);
33903         el.on("dblclick", this.onDblClick, this);
33904         el.on("contextmenu", this.onContextMenu, this);
33905         
33906     },
33907     
33908     /*onSelectedChange : function(state){
33909         if(state){
33910             this.focus();
33911             this.addClass("x-tree-selected");
33912         }else{
33913             //this.blur();
33914             this.removeClass("x-tree-selected");
33915         }
33916     },*/
33917     addClass : function(cls){
33918         if(this.elRow){
33919             Roo.fly(this.elRow).addClass(cls);
33920         }
33921         
33922     },
33923     
33924     
33925     removeClass : function(cls){
33926         if(this.elRow){
33927             Roo.fly(this.elRow).removeClass(cls);
33928         }
33929     }
33930
33931     
33932     
33933 });//<Script type="text/javascript">
33934
33935 /*
33936  * Based on:
33937  * Ext JS Library 1.1.1
33938  * Copyright(c) 2006-2007, Ext JS, LLC.
33939  *
33940  * Originally Released Under LGPL - original licence link has changed is not relivant.
33941  *
33942  * Fork - LGPL
33943  * <script type="text/javascript">
33944  */
33945  
33946
33947 /**
33948  * @class Roo.tree.ColumnTree
33949  * @extends Roo.data.TreePanel
33950  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33951  * @cfg {int} borderWidth  compined right/left border allowance
33952  * @constructor
33953  * @param {String/HTMLElement/Element} el The container element
33954  * @param {Object} config
33955  */
33956 Roo.tree.ColumnTree =  function(el, config)
33957 {
33958    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33959    this.addEvents({
33960         /**
33961         * @event resize
33962         * Fire this event on a container when it resizes
33963         * @param {int} w Width
33964         * @param {int} h Height
33965         */
33966        "resize" : true
33967     });
33968     this.on('resize', this.onResize, this);
33969 };
33970
33971 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33972     //lines:false,
33973     
33974     
33975     borderWidth: Roo.isBorderBox ? 0 : 2, 
33976     headEls : false,
33977     
33978     render : function(){
33979         // add the header.....
33980        
33981         Roo.tree.ColumnTree.superclass.render.apply(this);
33982         
33983         this.el.addClass('x-column-tree');
33984         
33985         this.headers = this.el.createChild(
33986             {cls:'x-tree-headers'},this.innerCt.dom);
33987    
33988         var cols = this.columns, c;
33989         var totalWidth = 0;
33990         this.headEls = [];
33991         var  len = cols.length;
33992         for(var i = 0; i < len; i++){
33993              c = cols[i];
33994              totalWidth += c.width;
33995             this.headEls.push(this.headers.createChild({
33996                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33997                  cn: {
33998                      cls:'x-tree-hd-text',
33999                      html: c.header
34000                  },
34001                  style:'width:'+(c.width-this.borderWidth)+'px;'
34002              }));
34003         }
34004         this.headers.createChild({cls:'x-clear'});
34005         // prevent floats from wrapping when clipped
34006         this.headers.setWidth(totalWidth);
34007         //this.innerCt.setWidth(totalWidth);
34008         this.innerCt.setStyle({ overflow: 'auto' });
34009         this.onResize(this.width, this.height);
34010              
34011         
34012     },
34013     onResize : function(w,h)
34014     {
34015         this.height = h;
34016         this.width = w;
34017         // resize cols..
34018         this.innerCt.setWidth(this.width);
34019         this.innerCt.setHeight(this.height-20);
34020         
34021         // headers...
34022         var cols = this.columns, c;
34023         var totalWidth = 0;
34024         var expEl = false;
34025         var len = cols.length;
34026         for(var i = 0; i < len; i++){
34027             c = cols[i];
34028             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34029                 // it's the expander..
34030                 expEl  = this.headEls[i];
34031                 continue;
34032             }
34033             totalWidth += c.width;
34034             
34035         }
34036         if (expEl) {
34037             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34038         }
34039         this.headers.setWidth(w-20);
34040
34041         
34042         
34043         
34044     }
34045 });
34046 /*
34047  * Based on:
34048  * Ext JS Library 1.1.1
34049  * Copyright(c) 2006-2007, Ext JS, LLC.
34050  *
34051  * Originally Released Under LGPL - original licence link has changed is not relivant.
34052  *
34053  * Fork - LGPL
34054  * <script type="text/javascript">
34055  */
34056  
34057 /**
34058  * @class Roo.menu.Menu
34059  * @extends Roo.util.Observable
34060  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34061  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34062  * @constructor
34063  * Creates a new Menu
34064  * @param {Object} config Configuration options
34065  */
34066 Roo.menu.Menu = function(config){
34067     Roo.apply(this, config);
34068     this.id = this.id || Roo.id();
34069     this.addEvents({
34070         /**
34071          * @event beforeshow
34072          * Fires before this menu is displayed
34073          * @param {Roo.menu.Menu} this
34074          */
34075         beforeshow : true,
34076         /**
34077          * @event beforehide
34078          * Fires before this menu is hidden
34079          * @param {Roo.menu.Menu} this
34080          */
34081         beforehide : true,
34082         /**
34083          * @event show
34084          * Fires after this menu is displayed
34085          * @param {Roo.menu.Menu} this
34086          */
34087         show : true,
34088         /**
34089          * @event hide
34090          * Fires after this menu is hidden
34091          * @param {Roo.menu.Menu} this
34092          */
34093         hide : true,
34094         /**
34095          * @event click
34096          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34097          * @param {Roo.menu.Menu} this
34098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34099          * @param {Roo.EventObject} e
34100          */
34101         click : true,
34102         /**
34103          * @event mouseover
34104          * Fires when the mouse is hovering over this menu
34105          * @param {Roo.menu.Menu} this
34106          * @param {Roo.EventObject} e
34107          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34108          */
34109         mouseover : true,
34110         /**
34111          * @event mouseout
34112          * Fires when the mouse exits this menu
34113          * @param {Roo.menu.Menu} this
34114          * @param {Roo.EventObject} e
34115          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34116          */
34117         mouseout : true,
34118         /**
34119          * @event itemclick
34120          * Fires when a menu item contained in this menu is clicked
34121          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34122          * @param {Roo.EventObject} e
34123          */
34124         itemclick: true
34125     });
34126     if (this.registerMenu) {
34127         Roo.menu.MenuMgr.register(this);
34128     }
34129     
34130     var mis = this.items;
34131     this.items = new Roo.util.MixedCollection();
34132     if(mis){
34133         this.add.apply(this, mis);
34134     }
34135 };
34136
34137 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34138     /**
34139      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34140      */
34141     minWidth : 120,
34142     /**
34143      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34144      * for bottom-right shadow (defaults to "sides")
34145      */
34146     shadow : "sides",
34147     /**
34148      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34149      * this menu (defaults to "tl-tr?")
34150      */
34151     subMenuAlign : "tl-tr?",
34152     /**
34153      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34154      * relative to its element of origin (defaults to "tl-bl?")
34155      */
34156     defaultAlign : "tl-bl?",
34157     /**
34158      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34159      */
34160     allowOtherMenus : false,
34161     /**
34162      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34163      */
34164     registerMenu : true,
34165
34166     hidden:true,
34167
34168     // private
34169     render : function(){
34170         if(this.el){
34171             return;
34172         }
34173         var el = this.el = new Roo.Layer({
34174             cls: "x-menu",
34175             shadow:this.shadow,
34176             constrain: false,
34177             parentEl: this.parentEl || document.body,
34178             zindex:15000
34179         });
34180
34181         this.keyNav = new Roo.menu.MenuNav(this);
34182
34183         if(this.plain){
34184             el.addClass("x-menu-plain");
34185         }
34186         if(this.cls){
34187             el.addClass(this.cls);
34188         }
34189         // generic focus element
34190         this.focusEl = el.createChild({
34191             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34192         });
34193         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34194         ul.on("click", this.onClick, this);
34195         ul.on("mouseover", this.onMouseOver, this);
34196         ul.on("mouseout", this.onMouseOut, this);
34197         this.items.each(function(item){
34198             var li = document.createElement("li");
34199             li.className = "x-menu-list-item";
34200             ul.dom.appendChild(li);
34201             item.render(li, this);
34202         }, this);
34203         this.ul = ul;
34204         this.autoWidth();
34205     },
34206
34207     // private
34208     autoWidth : function(){
34209         var el = this.el, ul = this.ul;
34210         if(!el){
34211             return;
34212         }
34213         var w = this.width;
34214         if(w){
34215             el.setWidth(w);
34216         }else if(Roo.isIE){
34217             el.setWidth(this.minWidth);
34218             var t = el.dom.offsetWidth; // force recalc
34219             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34220         }
34221     },
34222
34223     // private
34224     delayAutoWidth : function(){
34225         if(this.rendered){
34226             if(!this.awTask){
34227                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34228             }
34229             this.awTask.delay(20);
34230         }
34231     },
34232
34233     // private
34234     findTargetItem : function(e){
34235         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34236         if(t && t.menuItemId){
34237             return this.items.get(t.menuItemId);
34238         }
34239     },
34240
34241     // private
34242     onClick : function(e){
34243         var t;
34244         if(t = this.findTargetItem(e)){
34245             t.onClick(e);
34246             this.fireEvent("click", this, t, e);
34247         }
34248     },
34249
34250     // private
34251     setActiveItem : function(item, autoExpand){
34252         if(item != this.activeItem){
34253             if(this.activeItem){
34254                 this.activeItem.deactivate();
34255             }
34256             this.activeItem = item;
34257             item.activate(autoExpand);
34258         }else if(autoExpand){
34259             item.expandMenu();
34260         }
34261     },
34262
34263     // private
34264     tryActivate : function(start, step){
34265         var items = this.items;
34266         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34267             var item = items.get(i);
34268             if(!item.disabled && item.canActivate){
34269                 this.setActiveItem(item, false);
34270                 return item;
34271             }
34272         }
34273         return false;
34274     },
34275
34276     // private
34277     onMouseOver : function(e){
34278         var t;
34279         if(t = this.findTargetItem(e)){
34280             if(t.canActivate && !t.disabled){
34281                 this.setActiveItem(t, true);
34282             }
34283         }
34284         this.fireEvent("mouseover", this, e, t);
34285     },
34286
34287     // private
34288     onMouseOut : function(e){
34289         var t;
34290         if(t = this.findTargetItem(e)){
34291             if(t == this.activeItem && t.shouldDeactivate(e)){
34292                 this.activeItem.deactivate();
34293                 delete this.activeItem;
34294             }
34295         }
34296         this.fireEvent("mouseout", this, e, t);
34297     },
34298
34299     /**
34300      * Read-only.  Returns true if the menu is currently displayed, else false.
34301      * @type Boolean
34302      */
34303     isVisible : function(){
34304         return this.el && !this.hidden;
34305     },
34306
34307     /**
34308      * Displays this menu relative to another element
34309      * @param {String/HTMLElement/Roo.Element} element The element to align to
34310      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34311      * the element (defaults to this.defaultAlign)
34312      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34313      */
34314     show : function(el, pos, parentMenu){
34315         this.parentMenu = parentMenu;
34316         if(!this.el){
34317             this.render();
34318         }
34319         this.fireEvent("beforeshow", this);
34320         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34321     },
34322
34323     /**
34324      * Displays this menu at a specific xy position
34325      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34326      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34327      */
34328     showAt : function(xy, parentMenu, /* private: */_e){
34329         this.parentMenu = parentMenu;
34330         if(!this.el){
34331             this.render();
34332         }
34333         if(_e !== false){
34334             this.fireEvent("beforeshow", this);
34335             xy = this.el.adjustForConstraints(xy);
34336         }
34337         this.el.setXY(xy);
34338         this.el.show();
34339         this.hidden = false;
34340         this.focus();
34341         this.fireEvent("show", this);
34342     },
34343
34344     focus : function(){
34345         if(!this.hidden){
34346             this.doFocus.defer(50, this);
34347         }
34348     },
34349
34350     doFocus : function(){
34351         if(!this.hidden){
34352             this.focusEl.focus();
34353         }
34354     },
34355
34356     /**
34357      * Hides this menu and optionally all parent menus
34358      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34359      */
34360     hide : function(deep){
34361         if(this.el && this.isVisible()){
34362             this.fireEvent("beforehide", this);
34363             if(this.activeItem){
34364                 this.activeItem.deactivate();
34365                 this.activeItem = null;
34366             }
34367             this.el.hide();
34368             this.hidden = true;
34369             this.fireEvent("hide", this);
34370         }
34371         if(deep === true && this.parentMenu){
34372             this.parentMenu.hide(true);
34373         }
34374     },
34375
34376     /**
34377      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34378      * Any of the following are valid:
34379      * <ul>
34380      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34381      * <li>An HTMLElement object which will be converted to a menu item</li>
34382      * <li>A menu item config object that will be created as a new menu item</li>
34383      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34384      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34385      * </ul>
34386      * Usage:
34387      * <pre><code>
34388 // Create the menu
34389 var menu = new Roo.menu.Menu();
34390
34391 // Create a menu item to add by reference
34392 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34393
34394 // Add a bunch of items at once using different methods.
34395 // Only the last item added will be returned.
34396 var item = menu.add(
34397     menuItem,                // add existing item by ref
34398     'Dynamic Item',          // new TextItem
34399     '-',                     // new separator
34400     { text: 'Config Item' }  // new item by config
34401 );
34402 </code></pre>
34403      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34404      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34405      */
34406     add : function(){
34407         var a = arguments, l = a.length, item;
34408         for(var i = 0; i < l; i++){
34409             var el = a[i];
34410             if ((typeof(el) == "object") && el.xtype && el.xns) {
34411                 el = Roo.factory(el, Roo.menu);
34412             }
34413             
34414             if(el.render){ // some kind of Item
34415                 item = this.addItem(el);
34416             }else if(typeof el == "string"){ // string
34417                 if(el == "separator" || el == "-"){
34418                     item = this.addSeparator();
34419                 }else{
34420                     item = this.addText(el);
34421                 }
34422             }else if(el.tagName || el.el){ // element
34423                 item = this.addElement(el);
34424             }else if(typeof el == "object"){ // must be menu item config?
34425                 item = this.addMenuItem(el);
34426             }
34427         }
34428         return item;
34429     },
34430
34431     /**
34432      * Returns this menu's underlying {@link Roo.Element} object
34433      * @return {Roo.Element} The element
34434      */
34435     getEl : function(){
34436         if(!this.el){
34437             this.render();
34438         }
34439         return this.el;
34440     },
34441
34442     /**
34443      * Adds a separator bar to the menu
34444      * @return {Roo.menu.Item} The menu item that was added
34445      */
34446     addSeparator : function(){
34447         return this.addItem(new Roo.menu.Separator());
34448     },
34449
34450     /**
34451      * Adds an {@link Roo.Element} object to the menu
34452      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34453      * @return {Roo.menu.Item} The menu item that was added
34454      */
34455     addElement : function(el){
34456         return this.addItem(new Roo.menu.BaseItem(el));
34457     },
34458
34459     /**
34460      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34461      * @param {Roo.menu.Item} item The menu item to add
34462      * @return {Roo.menu.Item} The menu item that was added
34463      */
34464     addItem : function(item){
34465         this.items.add(item);
34466         if(this.ul){
34467             var li = document.createElement("li");
34468             li.className = "x-menu-list-item";
34469             this.ul.dom.appendChild(li);
34470             item.render(li, this);
34471             this.delayAutoWidth();
34472         }
34473         return item;
34474     },
34475
34476     /**
34477      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34478      * @param {Object} config A MenuItem config object
34479      * @return {Roo.menu.Item} The menu item that was added
34480      */
34481     addMenuItem : function(config){
34482         if(!(config instanceof Roo.menu.Item)){
34483             if(typeof config.checked == "boolean"){ // must be check menu item config?
34484                 config = new Roo.menu.CheckItem(config);
34485             }else{
34486                 config = new Roo.menu.Item(config);
34487             }
34488         }
34489         return this.addItem(config);
34490     },
34491
34492     /**
34493      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34494      * @param {String} text The text to display in the menu item
34495      * @return {Roo.menu.Item} The menu item that was added
34496      */
34497     addText : function(text){
34498         return this.addItem(new Roo.menu.TextItem({ text : text }));
34499     },
34500
34501     /**
34502      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34503      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34504      * @param {Roo.menu.Item} item The menu item to add
34505      * @return {Roo.menu.Item} The menu item that was added
34506      */
34507     insert : function(index, item){
34508         this.items.insert(index, item);
34509         if(this.ul){
34510             var li = document.createElement("li");
34511             li.className = "x-menu-list-item";
34512             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34513             item.render(li, this);
34514             this.delayAutoWidth();
34515         }
34516         return item;
34517     },
34518
34519     /**
34520      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34521      * @param {Roo.menu.Item} item The menu item to remove
34522      */
34523     remove : function(item){
34524         this.items.removeKey(item.id);
34525         item.destroy();
34526     },
34527
34528     /**
34529      * Removes and destroys all items in the menu
34530      */
34531     removeAll : function(){
34532         var f;
34533         while(f = this.items.first()){
34534             this.remove(f);
34535         }
34536     }
34537 });
34538
34539 // MenuNav is a private utility class used internally by the Menu
34540 Roo.menu.MenuNav = function(menu){
34541     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34542     this.scope = this.menu = menu;
34543 };
34544
34545 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34546     doRelay : function(e, h){
34547         var k = e.getKey();
34548         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34549             this.menu.tryActivate(0, 1);
34550             return false;
34551         }
34552         return h.call(this.scope || this, e, this.menu);
34553     },
34554
34555     up : function(e, m){
34556         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34557             m.tryActivate(m.items.length-1, -1);
34558         }
34559     },
34560
34561     down : function(e, m){
34562         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34563             m.tryActivate(0, 1);
34564         }
34565     },
34566
34567     right : function(e, m){
34568         if(m.activeItem){
34569             m.activeItem.expandMenu(true);
34570         }
34571     },
34572
34573     left : function(e, m){
34574         m.hide();
34575         if(m.parentMenu && m.parentMenu.activeItem){
34576             m.parentMenu.activeItem.activate();
34577         }
34578     },
34579
34580     enter : function(e, m){
34581         if(m.activeItem){
34582             e.stopPropagation();
34583             m.activeItem.onClick(e);
34584             m.fireEvent("click", this, m.activeItem);
34585             return true;
34586         }
34587     }
34588 });/*
34589  * Based on:
34590  * Ext JS Library 1.1.1
34591  * Copyright(c) 2006-2007, Ext JS, LLC.
34592  *
34593  * Originally Released Under LGPL - original licence link has changed is not relivant.
34594  *
34595  * Fork - LGPL
34596  * <script type="text/javascript">
34597  */
34598  
34599 /**
34600  * @class Roo.menu.MenuMgr
34601  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34602  * @singleton
34603  */
34604 Roo.menu.MenuMgr = function(){
34605    var menus, active, groups = {}, attached = false, lastShow = new Date();
34606
34607    // private - called when first menu is created
34608    function init(){
34609        menus = {};
34610        active = new Roo.util.MixedCollection();
34611        Roo.get(document).addKeyListener(27, function(){
34612            if(active.length > 0){
34613                hideAll();
34614            }
34615        });
34616    }
34617
34618    // private
34619    function hideAll(){
34620        if(active && active.length > 0){
34621            var c = active.clone();
34622            c.each(function(m){
34623                m.hide();
34624            });
34625        }
34626    }
34627
34628    // private
34629    function onHide(m){
34630        active.remove(m);
34631        if(active.length < 1){
34632            Roo.get(document).un("mousedown", onMouseDown);
34633            attached = false;
34634        }
34635    }
34636
34637    // private
34638    function onShow(m){
34639        var last = active.last();
34640        lastShow = new Date();
34641        active.add(m);
34642        if(!attached){
34643            Roo.get(document).on("mousedown", onMouseDown);
34644            attached = true;
34645        }
34646        if(m.parentMenu){
34647           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34648           m.parentMenu.activeChild = m;
34649        }else if(last && last.isVisible()){
34650           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34651        }
34652    }
34653
34654    // private
34655    function onBeforeHide(m){
34656        if(m.activeChild){
34657            m.activeChild.hide();
34658        }
34659        if(m.autoHideTimer){
34660            clearTimeout(m.autoHideTimer);
34661            delete m.autoHideTimer;
34662        }
34663    }
34664
34665    // private
34666    function onBeforeShow(m){
34667        var pm = m.parentMenu;
34668        if(!pm && !m.allowOtherMenus){
34669            hideAll();
34670        }else if(pm && pm.activeChild && active != m){
34671            pm.activeChild.hide();
34672        }
34673    }
34674
34675    // private
34676    function onMouseDown(e){
34677        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34678            hideAll();
34679        }
34680    }
34681
34682    // private
34683    function onBeforeCheck(mi, state){
34684        if(state){
34685            var g = groups[mi.group];
34686            for(var i = 0, l = g.length; i < l; i++){
34687                if(g[i] != mi){
34688                    g[i].setChecked(false);
34689                }
34690            }
34691        }
34692    }
34693
34694    return {
34695
34696        /**
34697         * Hides all menus that are currently visible
34698         */
34699        hideAll : function(){
34700             hideAll();  
34701        },
34702
34703        // private
34704        register : function(menu){
34705            if(!menus){
34706                init();
34707            }
34708            menus[menu.id] = menu;
34709            menu.on("beforehide", onBeforeHide);
34710            menu.on("hide", onHide);
34711            menu.on("beforeshow", onBeforeShow);
34712            menu.on("show", onShow);
34713            var g = menu.group;
34714            if(g && menu.events["checkchange"]){
34715                if(!groups[g]){
34716                    groups[g] = [];
34717                }
34718                groups[g].push(menu);
34719                menu.on("checkchange", onCheck);
34720            }
34721        },
34722
34723         /**
34724          * Returns a {@link Roo.menu.Menu} object
34725          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34726          * be used to generate and return a new Menu instance.
34727          */
34728        get : function(menu){
34729            if(typeof menu == "string"){ // menu id
34730                return menus[menu];
34731            }else if(menu.events){  // menu instance
34732                return menu;
34733            }else if(typeof menu.length == 'number'){ // array of menu items?
34734                return new Roo.menu.Menu({items:menu});
34735            }else{ // otherwise, must be a config
34736                return new Roo.menu.Menu(menu);
34737            }
34738        },
34739
34740        // private
34741        unregister : function(menu){
34742            delete menus[menu.id];
34743            menu.un("beforehide", onBeforeHide);
34744            menu.un("hide", onHide);
34745            menu.un("beforeshow", onBeforeShow);
34746            menu.un("show", onShow);
34747            var g = menu.group;
34748            if(g && menu.events["checkchange"]){
34749                groups[g].remove(menu);
34750                menu.un("checkchange", onCheck);
34751            }
34752        },
34753
34754        // private
34755        registerCheckable : function(menuItem){
34756            var g = menuItem.group;
34757            if(g){
34758                if(!groups[g]){
34759                    groups[g] = [];
34760                }
34761                groups[g].push(menuItem);
34762                menuItem.on("beforecheckchange", onBeforeCheck);
34763            }
34764        },
34765
34766        // private
34767        unregisterCheckable : function(menuItem){
34768            var g = menuItem.group;
34769            if(g){
34770                groups[g].remove(menuItem);
34771                menuItem.un("beforecheckchange", onBeforeCheck);
34772            }
34773        }
34774    };
34775 }();/*
34776  * Based on:
34777  * Ext JS Library 1.1.1
34778  * Copyright(c) 2006-2007, Ext JS, LLC.
34779  *
34780  * Originally Released Under LGPL - original licence link has changed is not relivant.
34781  *
34782  * Fork - LGPL
34783  * <script type="text/javascript">
34784  */
34785  
34786
34787 /**
34788  * @class Roo.menu.BaseItem
34789  * @extends Roo.Component
34790  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34791  * management and base configuration options shared by all menu components.
34792  * @constructor
34793  * Creates a new BaseItem
34794  * @param {Object} config Configuration options
34795  */
34796 Roo.menu.BaseItem = function(config){
34797     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34798
34799     this.addEvents({
34800         /**
34801          * @event click
34802          * Fires when this item is clicked
34803          * @param {Roo.menu.BaseItem} this
34804          * @param {Roo.EventObject} e
34805          */
34806         click: true,
34807         /**
34808          * @event activate
34809          * Fires when this item is activated
34810          * @param {Roo.menu.BaseItem} this
34811          */
34812         activate : true,
34813         /**
34814          * @event deactivate
34815          * Fires when this item is deactivated
34816          * @param {Roo.menu.BaseItem} this
34817          */
34818         deactivate : true
34819     });
34820
34821     if(this.handler){
34822         this.on("click", this.handler, this.scope, true);
34823     }
34824 };
34825
34826 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34827     /**
34828      * @cfg {Function} handler
34829      * A function that will handle the click event of this menu item (defaults to undefined)
34830      */
34831     /**
34832      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34833      */
34834     canActivate : false,
34835     /**
34836      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34837      */
34838     activeClass : "x-menu-item-active",
34839     /**
34840      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34841      */
34842     hideOnClick : true,
34843     /**
34844      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34845      */
34846     hideDelay : 100,
34847
34848     // private
34849     ctype: "Roo.menu.BaseItem",
34850
34851     // private
34852     actionMode : "container",
34853
34854     // private
34855     render : function(container, parentMenu){
34856         this.parentMenu = parentMenu;
34857         Roo.menu.BaseItem.superclass.render.call(this, container);
34858         this.container.menuItemId = this.id;
34859     },
34860
34861     // private
34862     onRender : function(container, position){
34863         this.el = Roo.get(this.el);
34864         container.dom.appendChild(this.el.dom);
34865     },
34866
34867     // private
34868     onClick : function(e){
34869         if(!this.disabled && this.fireEvent("click", this, e) !== false
34870                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34871             this.handleClick(e);
34872         }else{
34873             e.stopEvent();
34874         }
34875     },
34876
34877     // private
34878     activate : function(){
34879         if(this.disabled){
34880             return false;
34881         }
34882         var li = this.container;
34883         li.addClass(this.activeClass);
34884         this.region = li.getRegion().adjust(2, 2, -2, -2);
34885         this.fireEvent("activate", this);
34886         return true;
34887     },
34888
34889     // private
34890     deactivate : function(){
34891         this.container.removeClass(this.activeClass);
34892         this.fireEvent("deactivate", this);
34893     },
34894
34895     // private
34896     shouldDeactivate : function(e){
34897         return !this.region || !this.region.contains(e.getPoint());
34898     },
34899
34900     // private
34901     handleClick : function(e){
34902         if(this.hideOnClick){
34903             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34904         }
34905     },
34906
34907     // private
34908     expandMenu : function(autoActivate){
34909         // do nothing
34910     },
34911
34912     // private
34913     hideMenu : function(){
34914         // do nothing
34915     }
34916 });/*
34917  * Based on:
34918  * Ext JS Library 1.1.1
34919  * Copyright(c) 2006-2007, Ext JS, LLC.
34920  *
34921  * Originally Released Under LGPL - original licence link has changed is not relivant.
34922  *
34923  * Fork - LGPL
34924  * <script type="text/javascript">
34925  */
34926  
34927 /**
34928  * @class Roo.menu.Adapter
34929  * @extends Roo.menu.BaseItem
34930  * 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.
34931  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34932  * @constructor
34933  * Creates a new Adapter
34934  * @param {Object} config Configuration options
34935  */
34936 Roo.menu.Adapter = function(component, config){
34937     Roo.menu.Adapter.superclass.constructor.call(this, config);
34938     this.component = component;
34939 };
34940 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34941     // private
34942     canActivate : true,
34943
34944     // private
34945     onRender : function(container, position){
34946         this.component.render(container);
34947         this.el = this.component.getEl();
34948     },
34949
34950     // private
34951     activate : function(){
34952         if(this.disabled){
34953             return false;
34954         }
34955         this.component.focus();
34956         this.fireEvent("activate", this);
34957         return true;
34958     },
34959
34960     // private
34961     deactivate : function(){
34962         this.fireEvent("deactivate", this);
34963     },
34964
34965     // private
34966     disable : function(){
34967         this.component.disable();
34968         Roo.menu.Adapter.superclass.disable.call(this);
34969     },
34970
34971     // private
34972     enable : function(){
34973         this.component.enable();
34974         Roo.menu.Adapter.superclass.enable.call(this);
34975     }
34976 });/*
34977  * Based on:
34978  * Ext JS Library 1.1.1
34979  * Copyright(c) 2006-2007, Ext JS, LLC.
34980  *
34981  * Originally Released Under LGPL - original licence link has changed is not relivant.
34982  *
34983  * Fork - LGPL
34984  * <script type="text/javascript">
34985  */
34986
34987 /**
34988  * @class Roo.menu.TextItem
34989  * @extends Roo.menu.BaseItem
34990  * Adds a static text string to a menu, usually used as either a heading or group separator.
34991  * Note: old style constructor with text is still supported.
34992  * 
34993  * @constructor
34994  * Creates a new TextItem
34995  * @param {Object} cfg Configuration
34996  */
34997 Roo.menu.TextItem = function(cfg){
34998     if (typeof(cfg) == 'string') {
34999         this.text = cfg;
35000     } else {
35001         Roo.apply(this,cfg);
35002     }
35003     
35004     Roo.menu.TextItem.superclass.constructor.call(this);
35005 };
35006
35007 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35008     /**
35009      * @cfg {Boolean} text Text to show on item.
35010      */
35011     text : '',
35012     
35013     /**
35014      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35015      */
35016     hideOnClick : false,
35017     /**
35018      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35019      */
35020     itemCls : "x-menu-text",
35021
35022     // private
35023     onRender : function(){
35024         var s = document.createElement("span");
35025         s.className = this.itemCls;
35026         s.innerHTML = this.text;
35027         this.el = s;
35028         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35029     }
35030 });/*
35031  * Based on:
35032  * Ext JS Library 1.1.1
35033  * Copyright(c) 2006-2007, Ext JS, LLC.
35034  *
35035  * Originally Released Under LGPL - original licence link has changed is not relivant.
35036  *
35037  * Fork - LGPL
35038  * <script type="text/javascript">
35039  */
35040
35041 /**
35042  * @class Roo.menu.Separator
35043  * @extends Roo.menu.BaseItem
35044  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35045  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35046  * @constructor
35047  * @param {Object} config Configuration options
35048  */
35049 Roo.menu.Separator = function(config){
35050     Roo.menu.Separator.superclass.constructor.call(this, config);
35051 };
35052
35053 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35054     /**
35055      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35056      */
35057     itemCls : "x-menu-sep",
35058     /**
35059      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35060      */
35061     hideOnClick : false,
35062
35063     // private
35064     onRender : function(li){
35065         var s = document.createElement("span");
35066         s.className = this.itemCls;
35067         s.innerHTML = "&#160;";
35068         this.el = s;
35069         li.addClass("x-menu-sep-li");
35070         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35071     }
35072 });/*
35073  * Based on:
35074  * Ext JS Library 1.1.1
35075  * Copyright(c) 2006-2007, Ext JS, LLC.
35076  *
35077  * Originally Released Under LGPL - original licence link has changed is not relivant.
35078  *
35079  * Fork - LGPL
35080  * <script type="text/javascript">
35081  */
35082 /**
35083  * @class Roo.menu.Item
35084  * @extends Roo.menu.BaseItem
35085  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35086  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35087  * activation and click handling.
35088  * @constructor
35089  * Creates a new Item
35090  * @param {Object} config Configuration options
35091  */
35092 Roo.menu.Item = function(config){
35093     Roo.menu.Item.superclass.constructor.call(this, config);
35094     if(this.menu){
35095         this.menu = Roo.menu.MenuMgr.get(this.menu);
35096     }
35097 };
35098 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35099     
35100     /**
35101      * @cfg {String} text
35102      * The text to show on the menu item.
35103      */
35104     text: '',
35105      /**
35106      * @cfg {String} HTML to render in menu
35107      * The text to show on the menu item (HTML version).
35108      */
35109     html: '',
35110     /**
35111      * @cfg {String} icon
35112      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35113      */
35114     icon: undefined,
35115     /**
35116      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35117      */
35118     itemCls : "x-menu-item",
35119     /**
35120      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35121      */
35122     canActivate : true,
35123     /**
35124      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35125      */
35126     showDelay: 200,
35127     // doc'd in BaseItem
35128     hideDelay: 200,
35129
35130     // private
35131     ctype: "Roo.menu.Item",
35132     
35133     // private
35134     onRender : function(container, position){
35135         var el = document.createElement("a");
35136         el.hideFocus = true;
35137         el.unselectable = "on";
35138         el.href = this.href || "#";
35139         if(this.hrefTarget){
35140             el.target = this.hrefTarget;
35141         }
35142         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35143         
35144         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35145         
35146         el.innerHTML = String.format(
35147                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35148                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35149         this.el = el;
35150         Roo.menu.Item.superclass.onRender.call(this, container, position);
35151     },
35152
35153     /**
35154      * Sets the text to display in this menu item
35155      * @param {String} text The text to display
35156      * @param {Boolean} isHTML true to indicate text is pure html.
35157      */
35158     setText : function(text, isHTML){
35159         if (isHTML) {
35160             this.html = text;
35161         } else {
35162             this.text = text;
35163             this.html = '';
35164         }
35165         if(this.rendered){
35166             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35167      
35168             this.el.update(String.format(
35169                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35170                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35171             this.parentMenu.autoWidth();
35172         }
35173     },
35174
35175     // private
35176     handleClick : function(e){
35177         if(!this.href){ // if no link defined, stop the event automatically
35178             e.stopEvent();
35179         }
35180         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35181     },
35182
35183     // private
35184     activate : function(autoExpand){
35185         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35186             this.focus();
35187             if(autoExpand){
35188                 this.expandMenu();
35189             }
35190         }
35191         return true;
35192     },
35193
35194     // private
35195     shouldDeactivate : function(e){
35196         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35197             if(this.menu && this.menu.isVisible()){
35198                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35199             }
35200             return true;
35201         }
35202         return false;
35203     },
35204
35205     // private
35206     deactivate : function(){
35207         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35208         this.hideMenu();
35209     },
35210
35211     // private
35212     expandMenu : function(autoActivate){
35213         if(!this.disabled && this.menu){
35214             clearTimeout(this.hideTimer);
35215             delete this.hideTimer;
35216             if(!this.menu.isVisible() && !this.showTimer){
35217                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35218             }else if (this.menu.isVisible() && autoActivate){
35219                 this.menu.tryActivate(0, 1);
35220             }
35221         }
35222     },
35223
35224     // private
35225     deferExpand : function(autoActivate){
35226         delete this.showTimer;
35227         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35228         if(autoActivate){
35229             this.menu.tryActivate(0, 1);
35230         }
35231     },
35232
35233     // private
35234     hideMenu : function(){
35235         clearTimeout(this.showTimer);
35236         delete this.showTimer;
35237         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35238             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35239         }
35240     },
35241
35242     // private
35243     deferHide : function(){
35244         delete this.hideTimer;
35245         this.menu.hide();
35246     }
35247 });/*
35248  * Based on:
35249  * Ext JS Library 1.1.1
35250  * Copyright(c) 2006-2007, Ext JS, LLC.
35251  *
35252  * Originally Released Under LGPL - original licence link has changed is not relivant.
35253  *
35254  * Fork - LGPL
35255  * <script type="text/javascript">
35256  */
35257  
35258 /**
35259  * @class Roo.menu.CheckItem
35260  * @extends Roo.menu.Item
35261  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35262  * @constructor
35263  * Creates a new CheckItem
35264  * @param {Object} config Configuration options
35265  */
35266 Roo.menu.CheckItem = function(config){
35267     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35268     this.addEvents({
35269         /**
35270          * @event beforecheckchange
35271          * Fires before the checked value is set, providing an opportunity to cancel if needed
35272          * @param {Roo.menu.CheckItem} this
35273          * @param {Boolean} checked The new checked value that will be set
35274          */
35275         "beforecheckchange" : true,
35276         /**
35277          * @event checkchange
35278          * Fires after the checked value has been set
35279          * @param {Roo.menu.CheckItem} this
35280          * @param {Boolean} checked The checked value that was set
35281          */
35282         "checkchange" : true
35283     });
35284     if(this.checkHandler){
35285         this.on('checkchange', this.checkHandler, this.scope);
35286     }
35287 };
35288 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35289     /**
35290      * @cfg {String} group
35291      * All check items with the same group name will automatically be grouped into a single-select
35292      * radio button group (defaults to '')
35293      */
35294     /**
35295      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35296      */
35297     itemCls : "x-menu-item x-menu-check-item",
35298     /**
35299      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35300      */
35301     groupClass : "x-menu-group-item",
35302
35303     /**
35304      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35305      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35306      * initialized with checked = true will be rendered as checked.
35307      */
35308     checked: false,
35309
35310     // private
35311     ctype: "Roo.menu.CheckItem",
35312
35313     // private
35314     onRender : function(c){
35315         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35316         if(this.group){
35317             this.el.addClass(this.groupClass);
35318         }
35319         Roo.menu.MenuMgr.registerCheckable(this);
35320         if(this.checked){
35321             this.checked = false;
35322             this.setChecked(true, true);
35323         }
35324     },
35325
35326     // private
35327     destroy : function(){
35328         if(this.rendered){
35329             Roo.menu.MenuMgr.unregisterCheckable(this);
35330         }
35331         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35332     },
35333
35334     /**
35335      * Set the checked state of this item
35336      * @param {Boolean} checked The new checked value
35337      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35338      */
35339     setChecked : function(state, suppressEvent){
35340         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35341             if(this.container){
35342                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35343             }
35344             this.checked = state;
35345             if(suppressEvent !== true){
35346                 this.fireEvent("checkchange", this, state);
35347             }
35348         }
35349     },
35350
35351     // private
35352     handleClick : function(e){
35353        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35354            this.setChecked(!this.checked);
35355        }
35356        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35357     }
35358 });/*
35359  * Based on:
35360  * Ext JS Library 1.1.1
35361  * Copyright(c) 2006-2007, Ext JS, LLC.
35362  *
35363  * Originally Released Under LGPL - original licence link has changed is not relivant.
35364  *
35365  * Fork - LGPL
35366  * <script type="text/javascript">
35367  */
35368  
35369 /**
35370  * @class Roo.menu.DateItem
35371  * @extends Roo.menu.Adapter
35372  * A menu item that wraps the {@link Roo.DatPicker} component.
35373  * @constructor
35374  * Creates a new DateItem
35375  * @param {Object} config Configuration options
35376  */
35377 Roo.menu.DateItem = function(config){
35378     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35379     /** The Roo.DatePicker object @type Roo.DatePicker */
35380     this.picker = this.component;
35381     this.addEvents({select: true});
35382     
35383     this.picker.on("render", function(picker){
35384         picker.getEl().swallowEvent("click");
35385         picker.container.addClass("x-menu-date-item");
35386     });
35387
35388     this.picker.on("select", this.onSelect, this);
35389 };
35390
35391 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35392     // private
35393     onSelect : function(picker, date){
35394         this.fireEvent("select", this, date, picker);
35395         Roo.menu.DateItem.superclass.handleClick.call(this);
35396     }
35397 });/*
35398  * Based on:
35399  * Ext JS Library 1.1.1
35400  * Copyright(c) 2006-2007, Ext JS, LLC.
35401  *
35402  * Originally Released Under LGPL - original licence link has changed is not relivant.
35403  *
35404  * Fork - LGPL
35405  * <script type="text/javascript">
35406  */
35407  
35408 /**
35409  * @class Roo.menu.ColorItem
35410  * @extends Roo.menu.Adapter
35411  * A menu item that wraps the {@link Roo.ColorPalette} component.
35412  * @constructor
35413  * Creates a new ColorItem
35414  * @param {Object} config Configuration options
35415  */
35416 Roo.menu.ColorItem = function(config){
35417     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35418     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35419     this.palette = this.component;
35420     this.relayEvents(this.palette, ["select"]);
35421     if(this.selectHandler){
35422         this.on('select', this.selectHandler, this.scope);
35423     }
35424 };
35425 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35426  * Based on:
35427  * Ext JS Library 1.1.1
35428  * Copyright(c) 2006-2007, Ext JS, LLC.
35429  *
35430  * Originally Released Under LGPL - original licence link has changed is not relivant.
35431  *
35432  * Fork - LGPL
35433  * <script type="text/javascript">
35434  */
35435  
35436
35437 /**
35438  * @class Roo.menu.DateMenu
35439  * @extends Roo.menu.Menu
35440  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35441  * @constructor
35442  * Creates a new DateMenu
35443  * @param {Object} config Configuration options
35444  */
35445 Roo.menu.DateMenu = function(config){
35446     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35447     this.plain = true;
35448     var di = new Roo.menu.DateItem(config);
35449     this.add(di);
35450     /**
35451      * The {@link Roo.DatePicker} instance for this DateMenu
35452      * @type DatePicker
35453      */
35454     this.picker = di.picker;
35455     /**
35456      * @event select
35457      * @param {DatePicker} picker
35458      * @param {Date} date
35459      */
35460     this.relayEvents(di, ["select"]);
35461
35462     this.on('beforeshow', function(){
35463         if(this.picker){
35464             this.picker.hideMonthPicker(true);
35465         }
35466     }, this);
35467 };
35468 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35469     cls:'x-date-menu'
35470 });/*
35471  * Based on:
35472  * Ext JS Library 1.1.1
35473  * Copyright(c) 2006-2007, Ext JS, LLC.
35474  *
35475  * Originally Released Under LGPL - original licence link has changed is not relivant.
35476  *
35477  * Fork - LGPL
35478  * <script type="text/javascript">
35479  */
35480  
35481
35482 /**
35483  * @class Roo.menu.ColorMenu
35484  * @extends Roo.menu.Menu
35485  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35486  * @constructor
35487  * Creates a new ColorMenu
35488  * @param {Object} config Configuration options
35489  */
35490 Roo.menu.ColorMenu = function(config){
35491     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35492     this.plain = true;
35493     var ci = new Roo.menu.ColorItem(config);
35494     this.add(ci);
35495     /**
35496      * The {@link Roo.ColorPalette} instance for this ColorMenu
35497      * @type ColorPalette
35498      */
35499     this.palette = ci.palette;
35500     /**
35501      * @event select
35502      * @param {ColorPalette} palette
35503      * @param {String} color
35504      */
35505     this.relayEvents(ci, ["select"]);
35506 };
35507 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
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  * @class Roo.form.Field
35520  * @extends Roo.BoxComponent
35521  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35522  * @constructor
35523  * Creates a new Field
35524  * @param {Object} config Configuration options
35525  */
35526 Roo.form.Field = function(config){
35527     Roo.form.Field.superclass.constructor.call(this, config);
35528 };
35529
35530 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35531     /**
35532      * @cfg {String} fieldLabel Label to use when rendering a form.
35533      */
35534        /**
35535      * @cfg {String} qtip Mouse over tip
35536      */
35537      
35538     /**
35539      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35540      */
35541     invalidClass : "x-form-invalid",
35542     /**
35543      * @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")
35544      */
35545     invalidText : "The value in this field is invalid",
35546     /**
35547      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35548      */
35549     focusClass : "x-form-focus",
35550     /**
35551      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35552       automatic validation (defaults to "keyup").
35553      */
35554     validationEvent : "keyup",
35555     /**
35556      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35557      */
35558     validateOnBlur : true,
35559     /**
35560      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35561      */
35562     validationDelay : 250,
35563     /**
35564      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35565      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35566      */
35567     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35568     /**
35569      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35570      */
35571     fieldClass : "x-form-field",
35572     /**
35573      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35574      *<pre>
35575 Value         Description
35576 -----------   ----------------------------------------------------------------------
35577 qtip          Display a quick tip when the user hovers over the field
35578 title         Display a default browser title attribute popup
35579 under         Add a block div beneath the field containing the error text
35580 side          Add an error icon to the right of the field with a popup on hover
35581 [element id]  Add the error text directly to the innerHTML of the specified element
35582 </pre>
35583      */
35584     msgTarget : 'qtip',
35585     /**
35586      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35587      */
35588     msgFx : 'normal',
35589
35590     /**
35591      * @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.
35592      */
35593     readOnly : false,
35594
35595     /**
35596      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35597      */
35598     disabled : false,
35599
35600     /**
35601      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35602      */
35603     inputType : undefined,
35604     
35605     /**
35606      * @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).
35607          */
35608         tabIndex : undefined,
35609         
35610     // private
35611     isFormField : true,
35612
35613     // private
35614     hasFocus : false,
35615     /**
35616      * @property {Roo.Element} fieldEl
35617      * Element Containing the rendered Field (with label etc.)
35618      */
35619     /**
35620      * @cfg {Mixed} value A value to initialize this field with.
35621      */
35622     value : undefined,
35623
35624     /**
35625      * @cfg {String} name The field's HTML name attribute.
35626      */
35627     /**
35628      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35629      */
35630
35631         // private ??
35632         initComponent : function(){
35633         Roo.form.Field.superclass.initComponent.call(this);
35634         this.addEvents({
35635             /**
35636              * @event focus
35637              * Fires when this field receives input focus.
35638              * @param {Roo.form.Field} this
35639              */
35640             focus : true,
35641             /**
35642              * @event blur
35643              * Fires when this field loses input focus.
35644              * @param {Roo.form.Field} this
35645              */
35646             blur : true,
35647             /**
35648              * @event specialkey
35649              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35650              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35651              * @param {Roo.form.Field} this
35652              * @param {Roo.EventObject} e The event object
35653              */
35654             specialkey : true,
35655             /**
35656              * @event change
35657              * Fires just before the field blurs if the field value has changed.
35658              * @param {Roo.form.Field} this
35659              * @param {Mixed} newValue The new value
35660              * @param {Mixed} oldValue The original value
35661              */
35662             change : true,
35663             /**
35664              * @event invalid
35665              * Fires after the field has been marked as invalid.
35666              * @param {Roo.form.Field} this
35667              * @param {String} msg The validation message
35668              */
35669             invalid : true,
35670             /**
35671              * @event valid
35672              * Fires after the field has been validated with no errors.
35673              * @param {Roo.form.Field} this
35674              */
35675             valid : true,
35676              /**
35677              * @event keyup
35678              * Fires after the key up
35679              * @param {Roo.form.Field} this
35680              * @param {Roo.EventObject}  e The event Object
35681              */
35682             keyup : true
35683         });
35684     },
35685
35686     /**
35687      * Returns the name attribute of the field if available
35688      * @return {String} name The field name
35689      */
35690     getName: function(){
35691          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35692     },
35693
35694     // private
35695     onRender : function(ct, position){
35696         Roo.form.Field.superclass.onRender.call(this, ct, position);
35697         if(!this.el){
35698             var cfg = this.getAutoCreate();
35699             if(!cfg.name){
35700                 cfg.name = this.name || this.id;
35701             }
35702             if(this.inputType){
35703                 cfg.type = this.inputType;
35704             }
35705             this.el = ct.createChild(cfg, position);
35706         }
35707         var type = this.el.dom.type;
35708         if(type){
35709             if(type == 'password'){
35710                 type = 'text';
35711             }
35712             this.el.addClass('x-form-'+type);
35713         }
35714         if(this.readOnly){
35715             this.el.dom.readOnly = true;
35716         }
35717         if(this.tabIndex !== undefined){
35718             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35719         }
35720
35721         this.el.addClass([this.fieldClass, this.cls]);
35722         this.initValue();
35723     },
35724
35725     /**
35726      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35727      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35728      * @return {Roo.form.Field} this
35729      */
35730     applyTo : function(target){
35731         this.allowDomMove = false;
35732         this.el = Roo.get(target);
35733         this.render(this.el.dom.parentNode);
35734         return this;
35735     },
35736
35737     // private
35738     initValue : function(){
35739         if(this.value !== undefined){
35740             this.setValue(this.value);
35741         }else if(this.el.dom.value.length > 0){
35742             this.setValue(this.el.dom.value);
35743         }
35744     },
35745
35746     /**
35747      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35748      */
35749     isDirty : function() {
35750         if(this.disabled) {
35751             return false;
35752         }
35753         return String(this.getValue()) !== String(this.originalValue);
35754     },
35755
35756     // private
35757     afterRender : function(){
35758         Roo.form.Field.superclass.afterRender.call(this);
35759         this.initEvents();
35760     },
35761
35762     // private
35763     fireKey : function(e){
35764         //Roo.log('field ' + e.getKey());
35765         if(e.isNavKeyPress()){
35766             this.fireEvent("specialkey", this, e);
35767         }
35768     },
35769
35770     /**
35771      * Resets the current field value to the originally loaded value and clears any validation messages
35772      */
35773     reset : function(){
35774         this.setValue(this.originalValue);
35775         this.clearInvalid();
35776     },
35777
35778     // private
35779     initEvents : function(){
35780         // safari killled keypress - so keydown is now used..
35781         this.el.on("keydown" , this.fireKey,  this);
35782         this.el.on("focus", this.onFocus,  this);
35783         this.el.on("blur", this.onBlur,  this);
35784         this.el.relayEvent('keyup', this);
35785
35786         // reference to original value for reset
35787         this.originalValue = this.getValue();
35788     },
35789
35790     // private
35791     onFocus : function(){
35792         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35793             this.el.addClass(this.focusClass);
35794         }
35795         if(!this.hasFocus){
35796             this.hasFocus = true;
35797             this.startValue = this.getValue();
35798             this.fireEvent("focus", this);
35799         }
35800     },
35801
35802     beforeBlur : Roo.emptyFn,
35803
35804     // private
35805     onBlur : function(){
35806         this.beforeBlur();
35807         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35808             this.el.removeClass(this.focusClass);
35809         }
35810         this.hasFocus = false;
35811         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35812             this.validate();
35813         }
35814         var v = this.getValue();
35815         if(String(v) !== String(this.startValue)){
35816             this.fireEvent('change', this, v, this.startValue);
35817         }
35818         this.fireEvent("blur", this);
35819     },
35820
35821     /**
35822      * Returns whether or not the field value is currently valid
35823      * @param {Boolean} preventMark True to disable marking the field invalid
35824      * @return {Boolean} True if the value is valid, else false
35825      */
35826     isValid : function(preventMark){
35827         if(this.disabled){
35828             return true;
35829         }
35830         var restore = this.preventMark;
35831         this.preventMark = preventMark === true;
35832         var v = this.validateValue(this.processValue(this.getRawValue()));
35833         this.preventMark = restore;
35834         return v;
35835     },
35836
35837     /**
35838      * Validates the field value
35839      * @return {Boolean} True if the value is valid, else false
35840      */
35841     validate : function(){
35842         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35843             this.clearInvalid();
35844             return true;
35845         }
35846         return false;
35847     },
35848
35849     processValue : function(value){
35850         return value;
35851     },
35852
35853     // private
35854     // Subclasses should provide the validation implementation by overriding this
35855     validateValue : function(value){
35856         return true;
35857     },
35858
35859     /**
35860      * Mark this field as invalid
35861      * @param {String} msg The validation message
35862      */
35863     markInvalid : function(msg){
35864         if(!this.rendered || this.preventMark){ // not rendered
35865             return;
35866         }
35867         this.el.addClass(this.invalidClass);
35868         msg = msg || this.invalidText;
35869         switch(this.msgTarget){
35870             case 'qtip':
35871                 this.el.dom.qtip = msg;
35872                 this.el.dom.qclass = 'x-form-invalid-tip';
35873                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35874                     Roo.QuickTips.enable();
35875                 }
35876                 break;
35877             case 'title':
35878                 this.el.dom.title = msg;
35879                 break;
35880             case 'under':
35881                 if(!this.errorEl){
35882                     var elp = this.el.findParent('.x-form-element', 5, true);
35883                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35884                     this.errorEl.setWidth(elp.getWidth(true)-20);
35885                 }
35886                 this.errorEl.update(msg);
35887                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35888                 break;
35889             case 'side':
35890                 if(!this.errorIcon){
35891                     var elp = this.el.findParent('.x-form-element', 5, true);
35892                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35893                 }
35894                 this.alignErrorIcon();
35895                 this.errorIcon.dom.qtip = msg;
35896                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35897                 this.errorIcon.show();
35898                 this.on('resize', this.alignErrorIcon, this);
35899                 break;
35900             default:
35901                 var t = Roo.getDom(this.msgTarget);
35902                 t.innerHTML = msg;
35903                 t.style.display = this.msgDisplay;
35904                 break;
35905         }
35906         this.fireEvent('invalid', this, msg);
35907     },
35908
35909     // private
35910     alignErrorIcon : function(){
35911         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35912     },
35913
35914     /**
35915      * Clear any invalid styles/messages for this field
35916      */
35917     clearInvalid : function(){
35918         if(!this.rendered || this.preventMark){ // not rendered
35919             return;
35920         }
35921         this.el.removeClass(this.invalidClass);
35922         switch(this.msgTarget){
35923             case 'qtip':
35924                 this.el.dom.qtip = '';
35925                 break;
35926             case 'title':
35927                 this.el.dom.title = '';
35928                 break;
35929             case 'under':
35930                 if(this.errorEl){
35931                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35932                 }
35933                 break;
35934             case 'side':
35935                 if(this.errorIcon){
35936                     this.errorIcon.dom.qtip = '';
35937                     this.errorIcon.hide();
35938                     this.un('resize', this.alignErrorIcon, this);
35939                 }
35940                 break;
35941             default:
35942                 var t = Roo.getDom(this.msgTarget);
35943                 t.innerHTML = '';
35944                 t.style.display = 'none';
35945                 break;
35946         }
35947         this.fireEvent('valid', this);
35948     },
35949
35950     /**
35951      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35952      * @return {Mixed} value The field value
35953      */
35954     getRawValue : function(){
35955         var v = this.el.getValue();
35956         if(v === this.emptyText){
35957             v = '';
35958         }
35959         return v;
35960     },
35961
35962     /**
35963      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35964      * @return {Mixed} value The field value
35965      */
35966     getValue : function(){
35967         var v = this.el.getValue();
35968         if(v === this.emptyText || v === undefined){
35969             v = '';
35970         }
35971         return v;
35972     },
35973
35974     /**
35975      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35976      * @param {Mixed} value The value to set
35977      */
35978     setRawValue : function(v){
35979         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35980     },
35981
35982     /**
35983      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35984      * @param {Mixed} value The value to set
35985      */
35986     setValue : function(v){
35987         this.value = v;
35988         if(this.rendered){
35989             this.el.dom.value = (v === null || v === undefined ? '' : v);
35990              this.validate();
35991         }
35992     },
35993
35994     adjustSize : function(w, h){
35995         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35996         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35997         return s;
35998     },
35999
36000     adjustWidth : function(tag, w){
36001         tag = tag.toLowerCase();
36002         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36003             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36004                 if(tag == 'input'){
36005                     return w + 2;
36006                 }
36007                 if(tag = 'textarea'){
36008                     return w-2;
36009                 }
36010             }else if(Roo.isOpera){
36011                 if(tag == 'input'){
36012                     return w + 2;
36013                 }
36014                 if(tag = 'textarea'){
36015                     return w-2;
36016                 }
36017             }
36018         }
36019         return w;
36020     }
36021 });
36022
36023
36024 // anything other than normal should be considered experimental
36025 Roo.form.Field.msgFx = {
36026     normal : {
36027         show: function(msgEl, f){
36028             msgEl.setDisplayed('block');
36029         },
36030
36031         hide : function(msgEl, f){
36032             msgEl.setDisplayed(false).update('');
36033         }
36034     },
36035
36036     slide : {
36037         show: function(msgEl, f){
36038             msgEl.slideIn('t', {stopFx:true});
36039         },
36040
36041         hide : function(msgEl, f){
36042             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36043         }
36044     },
36045
36046     slideRight : {
36047         show: function(msgEl, f){
36048             msgEl.fixDisplay();
36049             msgEl.alignTo(f.el, 'tl-tr');
36050             msgEl.slideIn('l', {stopFx:true});
36051         },
36052
36053         hide : function(msgEl, f){
36054             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36055         }
36056     }
36057 };/*
36058  * Based on:
36059  * Ext JS Library 1.1.1
36060  * Copyright(c) 2006-2007, Ext JS, LLC.
36061  *
36062  * Originally Released Under LGPL - original licence link has changed is not relivant.
36063  *
36064  * Fork - LGPL
36065  * <script type="text/javascript">
36066  */
36067  
36068
36069 /**
36070  * @class Roo.form.TextField
36071  * @extends Roo.form.Field
36072  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36073  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36074  * @constructor
36075  * Creates a new TextField
36076  * @param {Object} config Configuration options
36077  */
36078 Roo.form.TextField = function(config){
36079     Roo.form.TextField.superclass.constructor.call(this, config);
36080     this.addEvents({
36081         /**
36082          * @event autosize
36083          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36084          * according to the default logic, but this event provides a hook for the developer to apply additional
36085          * logic at runtime to resize the field if needed.
36086              * @param {Roo.form.Field} this This text field
36087              * @param {Number} width The new field width
36088              */
36089         autosize : true
36090     });
36091 };
36092
36093 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36094     /**
36095      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36096      */
36097     grow : false,
36098     /**
36099      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36100      */
36101     growMin : 30,
36102     /**
36103      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36104      */
36105     growMax : 800,
36106     /**
36107      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36108      */
36109     vtype : null,
36110     /**
36111      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36112      */
36113     maskRe : null,
36114     /**
36115      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36116      */
36117     disableKeyFilter : false,
36118     /**
36119      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36120      */
36121     allowBlank : true,
36122     /**
36123      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36124      */
36125     minLength : 0,
36126     /**
36127      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36128      */
36129     maxLength : Number.MAX_VALUE,
36130     /**
36131      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36132      */
36133     minLengthText : "The minimum length for this field is {0}",
36134     /**
36135      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36136      */
36137     maxLengthText : "The maximum length for this field is {0}",
36138     /**
36139      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36140      */
36141     selectOnFocus : false,
36142     /**
36143      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36144      */
36145     blankText : "This field is required",
36146     /**
36147      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36148      * If available, this function will be called only after the basic validators all return true, and will be passed the
36149      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36150      */
36151     validator : null,
36152     /**
36153      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36154      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36155      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36156      */
36157     regex : null,
36158     /**
36159      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36160      */
36161     regexText : "",
36162     /**
36163      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36164      */
36165     emptyText : null,
36166     /**
36167      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36168      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36169      */
36170     emptyClass : 'x-form-empty-field',
36171
36172     // private
36173     initEvents : function(){
36174         Roo.form.TextField.superclass.initEvents.call(this);
36175         if(this.validationEvent == 'keyup'){
36176             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36177             this.el.on('keyup', this.filterValidation, this);
36178         }
36179         else if(this.validationEvent !== false){
36180             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36181         }
36182         if(this.selectOnFocus || this.emptyText){
36183             this.on("focus", this.preFocus, this);
36184             if(this.emptyText){
36185                 this.on('blur', this.postBlur, this);
36186                 this.applyEmptyText();
36187             }
36188         }
36189         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36190             this.el.on("keypress", this.filterKeys, this);
36191         }
36192         if(this.grow){
36193             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36194             this.el.on("click", this.autoSize,  this);
36195         }
36196     },
36197
36198     processValue : function(value){
36199         if(this.stripCharsRe){
36200             var newValue = value.replace(this.stripCharsRe, '');
36201             if(newValue !== value){
36202                 this.setRawValue(newValue);
36203                 return newValue;
36204             }
36205         }
36206         return value;
36207     },
36208
36209     filterValidation : function(e){
36210         if(!e.isNavKeyPress()){
36211             this.validationTask.delay(this.validationDelay);
36212         }
36213     },
36214
36215     // private
36216     onKeyUp : function(e){
36217         if(!e.isNavKeyPress()){
36218             this.autoSize();
36219         }
36220     },
36221
36222     /**
36223      * Resets the current field value to the originally-loaded value and clears any validation messages.
36224      * Also adds emptyText and emptyClass if the original value was blank.
36225      */
36226     reset : function(){
36227         Roo.form.TextField.superclass.reset.call(this);
36228         this.applyEmptyText();
36229     },
36230
36231     applyEmptyText : function(){
36232         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36233             this.setRawValue(this.emptyText);
36234             this.el.addClass(this.emptyClass);
36235         }
36236     },
36237
36238     // private
36239     preFocus : function(){
36240         if(this.emptyText){
36241             if(this.el.dom.value == this.emptyText){
36242                 this.setRawValue('');
36243             }
36244             this.el.removeClass(this.emptyClass);
36245         }
36246         if(this.selectOnFocus){
36247             this.el.dom.select();
36248         }
36249     },
36250
36251     // private
36252     postBlur : function(){
36253         this.applyEmptyText();
36254     },
36255
36256     // private
36257     filterKeys : function(e){
36258         var k = e.getKey();
36259         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36260             return;
36261         }
36262         var c = e.getCharCode(), cc = String.fromCharCode(c);
36263         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36264             return;
36265         }
36266         if(!this.maskRe.test(cc)){
36267             e.stopEvent();
36268         }
36269     },
36270
36271     setValue : function(v){
36272         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36273             this.el.removeClass(this.emptyClass);
36274         }
36275         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36276         this.applyEmptyText();
36277         this.autoSize();
36278     },
36279
36280     /**
36281      * Validates a value according to the field's validation rules and marks the field as invalid
36282      * if the validation fails
36283      * @param {Mixed} value The value to validate
36284      * @return {Boolean} True if the value is valid, else false
36285      */
36286     validateValue : function(value){
36287         if(value.length < 1 || value === this.emptyText){ // if it's blank
36288              if(this.allowBlank){
36289                 this.clearInvalid();
36290                 return true;
36291              }else{
36292                 this.markInvalid(this.blankText);
36293                 return false;
36294              }
36295         }
36296         if(value.length < this.minLength){
36297             this.markInvalid(String.format(this.minLengthText, this.minLength));
36298             return false;
36299         }
36300         if(value.length > this.maxLength){
36301             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36302             return false;
36303         }
36304         if(this.vtype){
36305             var vt = Roo.form.VTypes;
36306             if(!vt[this.vtype](value, this)){
36307                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36308                 return false;
36309             }
36310         }
36311         if(typeof this.validator == "function"){
36312             var msg = this.validator(value);
36313             if(msg !== true){
36314                 this.markInvalid(msg);
36315                 return false;
36316             }
36317         }
36318         if(this.regex && !this.regex.test(value)){
36319             this.markInvalid(this.regexText);
36320             return false;
36321         }
36322         return true;
36323     },
36324
36325     /**
36326      * Selects text in this field
36327      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36328      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36329      */
36330     selectText : function(start, end){
36331         var v = this.getRawValue();
36332         if(v.length > 0){
36333             start = start === undefined ? 0 : start;
36334             end = end === undefined ? v.length : end;
36335             var d = this.el.dom;
36336             if(d.setSelectionRange){
36337                 d.setSelectionRange(start, end);
36338             }else if(d.createTextRange){
36339                 var range = d.createTextRange();
36340                 range.moveStart("character", start);
36341                 range.moveEnd("character", v.length-end);
36342                 range.select();
36343             }
36344         }
36345     },
36346
36347     /**
36348      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36349      * This only takes effect if grow = true, and fires the autosize event.
36350      */
36351     autoSize : function(){
36352         if(!this.grow || !this.rendered){
36353             return;
36354         }
36355         if(!this.metrics){
36356             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36357         }
36358         var el = this.el;
36359         var v = el.dom.value;
36360         var d = document.createElement('div');
36361         d.appendChild(document.createTextNode(v));
36362         v = d.innerHTML;
36363         d = null;
36364         v += "&#160;";
36365         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36366         this.el.setWidth(w);
36367         this.fireEvent("autosize", this, w);
36368     }
36369 });/*
36370  * Based on:
36371  * Ext JS Library 1.1.1
36372  * Copyright(c) 2006-2007, Ext JS, LLC.
36373  *
36374  * Originally Released Under LGPL - original licence link has changed is not relivant.
36375  *
36376  * Fork - LGPL
36377  * <script type="text/javascript">
36378  */
36379  
36380 /**
36381  * @class Roo.form.Hidden
36382  * @extends Roo.form.TextField
36383  * Simple Hidden element used on forms 
36384  * 
36385  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36386  * 
36387  * @constructor
36388  * Creates a new Hidden form element.
36389  * @param {Object} config Configuration options
36390  */
36391
36392
36393
36394 // easy hidden field...
36395 Roo.form.Hidden = function(config){
36396     Roo.form.Hidden.superclass.constructor.call(this, config);
36397 };
36398   
36399 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36400     fieldLabel:      '',
36401     inputType:      'hidden',
36402     width:          50,
36403     allowBlank:     true,
36404     labelSeparator: '',
36405     hidden:         true,
36406     itemCls :       'x-form-item-display-none'
36407
36408
36409 });
36410
36411
36412 /*
36413  * Based on:
36414  * Ext JS Library 1.1.1
36415  * Copyright(c) 2006-2007, Ext JS, LLC.
36416  *
36417  * Originally Released Under LGPL - original licence link has changed is not relivant.
36418  *
36419  * Fork - LGPL
36420  * <script type="text/javascript">
36421  */
36422  
36423 /**
36424  * @class Roo.form.TriggerField
36425  * @extends Roo.form.TextField
36426  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36427  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36428  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36429  * for which you can provide a custom implementation.  For example:
36430  * <pre><code>
36431 var trigger = new Roo.form.TriggerField();
36432 trigger.onTriggerClick = myTriggerFn;
36433 trigger.applyTo('my-field');
36434 </code></pre>
36435  *
36436  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36437  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36438  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36439  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36440  * @constructor
36441  * Create a new TriggerField.
36442  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36443  * to the base TextField)
36444  */
36445 Roo.form.TriggerField = function(config){
36446     this.mimicing = false;
36447     Roo.form.TriggerField.superclass.constructor.call(this, config);
36448 };
36449
36450 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36451     /**
36452      * @cfg {String} triggerClass A CSS class to apply to the trigger
36453      */
36454     /**
36455      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36456      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36457      */
36458     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36459     /**
36460      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36461      */
36462     hideTrigger:false,
36463
36464     /** @cfg {Boolean} grow @hide */
36465     /** @cfg {Number} growMin @hide */
36466     /** @cfg {Number} growMax @hide */
36467
36468     /**
36469      * @hide 
36470      * @method
36471      */
36472     autoSize: Roo.emptyFn,
36473     // private
36474     monitorTab : true,
36475     // private
36476     deferHeight : true,
36477
36478     
36479     actionMode : 'wrap',
36480     // private
36481     onResize : function(w, h){
36482         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36483         if(typeof w == 'number'){
36484             var x = w - this.trigger.getWidth();
36485             this.el.setWidth(this.adjustWidth('input', x));
36486             this.trigger.setStyle('left', x+'px');
36487         }
36488     },
36489
36490     // private
36491     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36492
36493     // private
36494     getResizeEl : function(){
36495         return this.wrap;
36496     },
36497
36498     // private
36499     getPositionEl : function(){
36500         return this.wrap;
36501     },
36502
36503     // private
36504     alignErrorIcon : function(){
36505         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36506     },
36507
36508     // private
36509     onRender : function(ct, position){
36510         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36511         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36512         this.trigger = this.wrap.createChild(this.triggerConfig ||
36513                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36514         if(this.hideTrigger){
36515             this.trigger.setDisplayed(false);
36516         }
36517         this.initTrigger();
36518         if(!this.width){
36519             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36520         }
36521     },
36522
36523     // private
36524     initTrigger : function(){
36525         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36526         this.trigger.addClassOnOver('x-form-trigger-over');
36527         this.trigger.addClassOnClick('x-form-trigger-click');
36528     },
36529
36530     // private
36531     onDestroy : function(){
36532         if(this.trigger){
36533             this.trigger.removeAllListeners();
36534             this.trigger.remove();
36535         }
36536         if(this.wrap){
36537             this.wrap.remove();
36538         }
36539         Roo.form.TriggerField.superclass.onDestroy.call(this);
36540     },
36541
36542     // private
36543     onFocus : function(){
36544         Roo.form.TriggerField.superclass.onFocus.call(this);
36545         if(!this.mimicing){
36546             this.wrap.addClass('x-trigger-wrap-focus');
36547             this.mimicing = true;
36548             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36549             if(this.monitorTab){
36550                 this.el.on("keydown", this.checkTab, this);
36551             }
36552         }
36553     },
36554
36555     // private
36556     checkTab : function(e){
36557         if(e.getKey() == e.TAB){
36558             this.triggerBlur();
36559         }
36560     },
36561
36562     // private
36563     onBlur : function(){
36564         // do nothing
36565     },
36566
36567     // private
36568     mimicBlur : function(e, t){
36569         if(!this.wrap.contains(t) && this.validateBlur()){
36570             this.triggerBlur();
36571         }
36572     },
36573
36574     // private
36575     triggerBlur : function(){
36576         this.mimicing = false;
36577         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36578         if(this.monitorTab){
36579             this.el.un("keydown", this.checkTab, this);
36580         }
36581         this.wrap.removeClass('x-trigger-wrap-focus');
36582         Roo.form.TriggerField.superclass.onBlur.call(this);
36583     },
36584
36585     // private
36586     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36587     validateBlur : function(e, t){
36588         return true;
36589     },
36590
36591     // private
36592     onDisable : function(){
36593         Roo.form.TriggerField.superclass.onDisable.call(this);
36594         if(this.wrap){
36595             this.wrap.addClass('x-item-disabled');
36596         }
36597     },
36598
36599     // private
36600     onEnable : function(){
36601         Roo.form.TriggerField.superclass.onEnable.call(this);
36602         if(this.wrap){
36603             this.wrap.removeClass('x-item-disabled');
36604         }
36605     },
36606
36607     // private
36608     onShow : function(){
36609         var ae = this.getActionEl();
36610         
36611         if(ae){
36612             ae.dom.style.display = '';
36613             ae.dom.style.visibility = 'visible';
36614         }
36615     },
36616
36617     // private
36618     
36619     onHide : function(){
36620         var ae = this.getActionEl();
36621         ae.dom.style.display = 'none';
36622     },
36623
36624     /**
36625      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36626      * by an implementing function.
36627      * @method
36628      * @param {EventObject} e
36629      */
36630     onTriggerClick : Roo.emptyFn
36631 });
36632
36633 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36634 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36635 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36636 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36637     initComponent : function(){
36638         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36639
36640         this.triggerConfig = {
36641             tag:'span', cls:'x-form-twin-triggers', cn:[
36642             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36643             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36644         ]};
36645     },
36646
36647     getTrigger : function(index){
36648         return this.triggers[index];
36649     },
36650
36651     initTrigger : function(){
36652         var ts = this.trigger.select('.x-form-trigger', true);
36653         this.wrap.setStyle('overflow', 'hidden');
36654         var triggerField = this;
36655         ts.each(function(t, all, index){
36656             t.hide = function(){
36657                 var w = triggerField.wrap.getWidth();
36658                 this.dom.style.display = 'none';
36659                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36660             };
36661             t.show = function(){
36662                 var w = triggerField.wrap.getWidth();
36663                 this.dom.style.display = '';
36664                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36665             };
36666             var triggerIndex = 'Trigger'+(index+1);
36667
36668             if(this['hide'+triggerIndex]){
36669                 t.dom.style.display = 'none';
36670             }
36671             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36672             t.addClassOnOver('x-form-trigger-over');
36673             t.addClassOnClick('x-form-trigger-click');
36674         }, this);
36675         this.triggers = ts.elements;
36676     },
36677
36678     onTrigger1Click : Roo.emptyFn,
36679     onTrigger2Click : Roo.emptyFn
36680 });/*
36681  * Based on:
36682  * Ext JS Library 1.1.1
36683  * Copyright(c) 2006-2007, Ext JS, LLC.
36684  *
36685  * Originally Released Under LGPL - original licence link has changed is not relivant.
36686  *
36687  * Fork - LGPL
36688  * <script type="text/javascript">
36689  */
36690  
36691 /**
36692  * @class Roo.form.TextArea
36693  * @extends Roo.form.TextField
36694  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36695  * support for auto-sizing.
36696  * @constructor
36697  * Creates a new TextArea
36698  * @param {Object} config Configuration options
36699  */
36700 Roo.form.TextArea = function(config){
36701     Roo.form.TextArea.superclass.constructor.call(this, config);
36702     // these are provided exchanges for backwards compat
36703     // minHeight/maxHeight were replaced by growMin/growMax to be
36704     // compatible with TextField growing config values
36705     if(this.minHeight !== undefined){
36706         this.growMin = this.minHeight;
36707     }
36708     if(this.maxHeight !== undefined){
36709         this.growMax = this.maxHeight;
36710     }
36711 };
36712
36713 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36714     /**
36715      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36716      */
36717     growMin : 60,
36718     /**
36719      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36720      */
36721     growMax: 1000,
36722     /**
36723      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36724      * in the field (equivalent to setting overflow: hidden, defaults to false)
36725      */
36726     preventScrollbars: false,
36727     /**
36728      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36729      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36730      */
36731
36732     // private
36733     onRender : function(ct, position){
36734         if(!this.el){
36735             this.defaultAutoCreate = {
36736                 tag: "textarea",
36737                 style:"width:300px;height:60px;",
36738                 autocomplete: "off"
36739             };
36740         }
36741         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36742         if(this.grow){
36743             this.textSizeEl = Roo.DomHelper.append(document.body, {
36744                 tag: "pre", cls: "x-form-grow-sizer"
36745             });
36746             if(this.preventScrollbars){
36747                 this.el.setStyle("overflow", "hidden");
36748             }
36749             this.el.setHeight(this.growMin);
36750         }
36751     },
36752
36753     onDestroy : function(){
36754         if(this.textSizeEl){
36755             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36756         }
36757         Roo.form.TextArea.superclass.onDestroy.call(this);
36758     },
36759
36760     // private
36761     onKeyUp : function(e){
36762         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36763             this.autoSize();
36764         }
36765     },
36766
36767     /**
36768      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36769      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36770      */
36771     autoSize : function(){
36772         if(!this.grow || !this.textSizeEl){
36773             return;
36774         }
36775         var el = this.el;
36776         var v = el.dom.value;
36777         var ts = this.textSizeEl;
36778
36779         ts.innerHTML = '';
36780         ts.appendChild(document.createTextNode(v));
36781         v = ts.innerHTML;
36782
36783         Roo.fly(ts).setWidth(this.el.getWidth());
36784         if(v.length < 1){
36785             v = "&#160;&#160;";
36786         }else{
36787             if(Roo.isIE){
36788                 v = v.replace(/\n/g, '<p>&#160;</p>');
36789             }
36790             v += "&#160;\n&#160;";
36791         }
36792         ts.innerHTML = v;
36793         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36794         if(h != this.lastHeight){
36795             this.lastHeight = h;
36796             this.el.setHeight(h);
36797             this.fireEvent("autosize", this, h);
36798         }
36799     }
36800 });/*
36801  * Based on:
36802  * Ext JS Library 1.1.1
36803  * Copyright(c) 2006-2007, Ext JS, LLC.
36804  *
36805  * Originally Released Under LGPL - original licence link has changed is not relivant.
36806  *
36807  * Fork - LGPL
36808  * <script type="text/javascript">
36809  */
36810  
36811
36812 /**
36813  * @class Roo.form.NumberField
36814  * @extends Roo.form.TextField
36815  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36816  * @constructor
36817  * Creates a new NumberField
36818  * @param {Object} config Configuration options
36819  */
36820 Roo.form.NumberField = function(config){
36821     Roo.form.NumberField.superclass.constructor.call(this, config);
36822 };
36823
36824 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36825     /**
36826      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36827      */
36828     fieldClass: "x-form-field x-form-num-field",
36829     /**
36830      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36831      */
36832     allowDecimals : true,
36833     /**
36834      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36835      */
36836     decimalSeparator : ".",
36837     /**
36838      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36839      */
36840     decimalPrecision : 2,
36841     /**
36842      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36843      */
36844     allowNegative : true,
36845     /**
36846      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36847      */
36848     minValue : Number.NEGATIVE_INFINITY,
36849     /**
36850      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36851      */
36852     maxValue : Number.MAX_VALUE,
36853     /**
36854      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36855      */
36856     minText : "The minimum value for this field is {0}",
36857     /**
36858      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36859      */
36860     maxText : "The maximum value for this field is {0}",
36861     /**
36862      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36863      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36864      */
36865     nanText : "{0} is not a valid number",
36866
36867     // private
36868     initEvents : function(){
36869         Roo.form.NumberField.superclass.initEvents.call(this);
36870         var allowed = "0123456789";
36871         if(this.allowDecimals){
36872             allowed += this.decimalSeparator;
36873         }
36874         if(this.allowNegative){
36875             allowed += "-";
36876         }
36877         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36878         var keyPress = function(e){
36879             var k = e.getKey();
36880             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36881                 return;
36882             }
36883             var c = e.getCharCode();
36884             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36885                 e.stopEvent();
36886             }
36887         };
36888         this.el.on("keypress", keyPress, this);
36889     },
36890
36891     // private
36892     validateValue : function(value){
36893         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36894             return false;
36895         }
36896         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36897              return true;
36898         }
36899         var num = this.parseValue(value);
36900         if(isNaN(num)){
36901             this.markInvalid(String.format(this.nanText, value));
36902             return false;
36903         }
36904         if(num < this.minValue){
36905             this.markInvalid(String.format(this.minText, this.minValue));
36906             return false;
36907         }
36908         if(num > this.maxValue){
36909             this.markInvalid(String.format(this.maxText, this.maxValue));
36910             return false;
36911         }
36912         return true;
36913     },
36914
36915     getValue : function(){
36916         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36917     },
36918
36919     // private
36920     parseValue : function(value){
36921         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36922         return isNaN(value) ? '' : value;
36923     },
36924
36925     // private
36926     fixPrecision : function(value){
36927         var nan = isNaN(value);
36928         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36929             return nan ? '' : value;
36930         }
36931         return parseFloat(value).toFixed(this.decimalPrecision);
36932     },
36933
36934     setValue : function(v){
36935         v = this.fixPrecision(v);
36936         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36937     },
36938
36939     // private
36940     decimalPrecisionFcn : function(v){
36941         return Math.floor(v);
36942     },
36943
36944     beforeBlur : function(){
36945         var v = this.parseValue(this.getRawValue());
36946         if(v){
36947             this.setValue(v);
36948         }
36949     }
36950 });/*
36951  * Based on:
36952  * Ext JS Library 1.1.1
36953  * Copyright(c) 2006-2007, Ext JS, LLC.
36954  *
36955  * Originally Released Under LGPL - original licence link has changed is not relivant.
36956  *
36957  * Fork - LGPL
36958  * <script type="text/javascript">
36959  */
36960  
36961 /**
36962  * @class Roo.form.DateField
36963  * @extends Roo.form.TriggerField
36964  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36965 * @constructor
36966 * Create a new DateField
36967 * @param {Object} config
36968  */
36969 Roo.form.DateField = function(config){
36970     Roo.form.DateField.superclass.constructor.call(this, config);
36971     
36972       this.addEvents({
36973          
36974         /**
36975          * @event select
36976          * Fires when a date is selected
36977              * @param {Roo.form.DateField} combo This combo box
36978              * @param {Date} date The date selected
36979              */
36980         'select' : true
36981          
36982     });
36983     
36984     
36985     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36986     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36987     this.ddMatch = null;
36988     if(this.disabledDates){
36989         var dd = this.disabledDates;
36990         var re = "(?:";
36991         for(var i = 0; i < dd.length; i++){
36992             re += dd[i];
36993             if(i != dd.length-1) re += "|";
36994         }
36995         this.ddMatch = new RegExp(re + ")");
36996     }
36997 };
36998
36999 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37000     /**
37001      * @cfg {String} format
37002      * The default date format string which can be overriden for localization support.  The format must be
37003      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37004      */
37005     format : "m/d/y",
37006     /**
37007      * @cfg {String} altFormats
37008      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37009      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37010      */
37011     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37012     /**
37013      * @cfg {Array} disabledDays
37014      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37015      */
37016     disabledDays : null,
37017     /**
37018      * @cfg {String} disabledDaysText
37019      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37020      */
37021     disabledDaysText : "Disabled",
37022     /**
37023      * @cfg {Array} disabledDates
37024      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37025      * expression so they are very powerful. Some examples:
37026      * <ul>
37027      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37028      * <li>["03/08", "09/16"] would disable those days for every year</li>
37029      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37030      * <li>["03/../2006"] would disable every day in March 2006</li>
37031      * <li>["^03"] would disable every day in every March</li>
37032      * </ul>
37033      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37034      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37035      */
37036     disabledDates : null,
37037     /**
37038      * @cfg {String} disabledDatesText
37039      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37040      */
37041     disabledDatesText : "Disabled",
37042     /**
37043      * @cfg {Date/String} minValue
37044      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37045      * valid format (defaults to null).
37046      */
37047     minValue : null,
37048     /**
37049      * @cfg {Date/String} maxValue
37050      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37051      * valid format (defaults to null).
37052      */
37053     maxValue : null,
37054     /**
37055      * @cfg {String} minText
37056      * The error text to display when the date in the cell is before minValue (defaults to
37057      * 'The date in this field must be after {minValue}').
37058      */
37059     minText : "The date in this field must be equal to or after {0}",
37060     /**
37061      * @cfg {String} maxText
37062      * The error text to display when the date in the cell is after maxValue (defaults to
37063      * 'The date in this field must be before {maxValue}').
37064      */
37065     maxText : "The date in this field must be equal to or before {0}",
37066     /**
37067      * @cfg {String} invalidText
37068      * The error text to display when the date in the field is invalid (defaults to
37069      * '{value} is not a valid date - it must be in the format {format}').
37070      */
37071     invalidText : "{0} is not a valid date - it must be in the format {1}",
37072     /**
37073      * @cfg {String} triggerClass
37074      * An additional CSS class used to style the trigger button.  The trigger will always get the
37075      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37076      * which displays a calendar icon).
37077      */
37078     triggerClass : 'x-form-date-trigger',
37079     
37080
37081     /**
37082      * @cfg {bool} useIso
37083      * if enabled, then the date field will use a hidden field to store the 
37084      * real value as iso formated date. default (false)
37085      */ 
37086     useIso : false,
37087     /**
37088      * @cfg {String/Object} autoCreate
37089      * A DomHelper element spec, or true for a default element spec (defaults to
37090      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37091      */ 
37092     // private
37093     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37094     
37095     // private
37096     hiddenField: false,
37097     
37098     onRender : function(ct, position)
37099     {
37100         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37101         if (this.useIso) {
37102             this.el.dom.removeAttribute('name'); 
37103             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37104                     'before', true);
37105             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37106             // prevent input submission
37107             this.hiddenName = this.name;
37108         }
37109             
37110             
37111     },
37112     
37113     // private
37114     validateValue : function(value)
37115     {
37116         value = this.formatDate(value);
37117         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37118             return false;
37119         }
37120         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37121              return true;
37122         }
37123         var svalue = value;
37124         value = this.parseDate(value);
37125         if(!value){
37126             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37127             return false;
37128         }
37129         var time = value.getTime();
37130         if(this.minValue && time < this.minValue.getTime()){
37131             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37132             return false;
37133         }
37134         if(this.maxValue && time > this.maxValue.getTime()){
37135             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37136             return false;
37137         }
37138         if(this.disabledDays){
37139             var day = value.getDay();
37140             for(var i = 0; i < this.disabledDays.length; i++) {
37141                 if(day === this.disabledDays[i]){
37142                     this.markInvalid(this.disabledDaysText);
37143                     return false;
37144                 }
37145             }
37146         }
37147         var fvalue = this.formatDate(value);
37148         if(this.ddMatch && this.ddMatch.test(fvalue)){
37149             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37150             return false;
37151         }
37152         return true;
37153     },
37154
37155     // private
37156     // Provides logic to override the default TriggerField.validateBlur which just returns true
37157     validateBlur : function(){
37158         return !this.menu || !this.menu.isVisible();
37159     },
37160
37161     /**
37162      * Returns the current date value of the date field.
37163      * @return {Date} The date value
37164      */
37165     getValue : function(){
37166         
37167         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37168     },
37169
37170     /**
37171      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37172      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37173      * (the default format used is "m/d/y").
37174      * <br />Usage:
37175      * <pre><code>
37176 //All of these calls set the same date value (May 4, 2006)
37177
37178 //Pass a date object:
37179 var dt = new Date('5/4/06');
37180 dateField.setValue(dt);
37181
37182 //Pass a date string (default format):
37183 dateField.setValue('5/4/06');
37184
37185 //Pass a date string (custom format):
37186 dateField.format = 'Y-m-d';
37187 dateField.setValue('2006-5-4');
37188 </code></pre>
37189      * @param {String/Date} date The date or valid date string
37190      */
37191     setValue : function(date){
37192         if (this.hiddenField) {
37193             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37194         }
37195         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37196     },
37197
37198     // private
37199     parseDate : function(value){
37200         if(!value || value instanceof Date){
37201             return value;
37202         }
37203         var v = Date.parseDate(value, this.format);
37204         if(!v && this.altFormats){
37205             if(!this.altFormatsArray){
37206                 this.altFormatsArray = this.altFormats.split("|");
37207             }
37208             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37209                 v = Date.parseDate(value, this.altFormatsArray[i]);
37210             }
37211         }
37212         return v;
37213     },
37214
37215     // private
37216     formatDate : function(date, fmt){
37217         return (!date || !(date instanceof Date)) ?
37218                date : date.dateFormat(fmt || this.format);
37219     },
37220
37221     // private
37222     menuListeners : {
37223         select: function(m, d){
37224             this.setValue(d);
37225             this.fireEvent('select', this, d);
37226         },
37227         show : function(){ // retain focus styling
37228             this.onFocus();
37229         },
37230         hide : function(){
37231             this.focus.defer(10, this);
37232             var ml = this.menuListeners;
37233             this.menu.un("select", ml.select,  this);
37234             this.menu.un("show", ml.show,  this);
37235             this.menu.un("hide", ml.hide,  this);
37236         }
37237     },
37238
37239     // private
37240     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37241     onTriggerClick : function(){
37242         if(this.disabled){
37243             return;
37244         }
37245         if(this.menu == null){
37246             this.menu = new Roo.menu.DateMenu();
37247         }
37248         Roo.apply(this.menu.picker,  {
37249             showClear: this.allowBlank,
37250             minDate : this.minValue,
37251             maxDate : this.maxValue,
37252             disabledDatesRE : this.ddMatch,
37253             disabledDatesText : this.disabledDatesText,
37254             disabledDays : this.disabledDays,
37255             disabledDaysText : this.disabledDaysText,
37256             format : this.format,
37257             minText : String.format(this.minText, this.formatDate(this.minValue)),
37258             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37259         });
37260         this.menu.on(Roo.apply({}, this.menuListeners, {
37261             scope:this
37262         }));
37263         this.menu.picker.setValue(this.getValue() || new Date());
37264         this.menu.show(this.el, "tl-bl?");
37265     },
37266
37267     beforeBlur : function(){
37268         var v = this.parseDate(this.getRawValue());
37269         if(v){
37270             this.setValue(v);
37271         }
37272     }
37273
37274     /** @cfg {Boolean} grow @hide */
37275     /** @cfg {Number} growMin @hide */
37276     /** @cfg {Number} growMax @hide */
37277     /**
37278      * @hide
37279      * @method autoSize
37280      */
37281 });/*
37282  * Based on:
37283  * Ext JS Library 1.1.1
37284  * Copyright(c) 2006-2007, Ext JS, LLC.
37285  *
37286  * Originally Released Under LGPL - original licence link has changed is not relivant.
37287  *
37288  * Fork - LGPL
37289  * <script type="text/javascript">
37290  */
37291  
37292
37293 /**
37294  * @class Roo.form.ComboBox
37295  * @extends Roo.form.TriggerField
37296  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37297  * @constructor
37298  * Create a new ComboBox.
37299  * @param {Object} config Configuration options
37300  */
37301 Roo.form.ComboBox = function(config){
37302     Roo.form.ComboBox.superclass.constructor.call(this, config);
37303     this.addEvents({
37304         /**
37305          * @event expand
37306          * Fires when the dropdown list is expanded
37307              * @param {Roo.form.ComboBox} combo This combo box
37308              */
37309         'expand' : true,
37310         /**
37311          * @event collapse
37312          * Fires when the dropdown list is collapsed
37313              * @param {Roo.form.ComboBox} combo This combo box
37314              */
37315         'collapse' : true,
37316         /**
37317          * @event beforeselect
37318          * Fires before a list item is selected. Return false to cancel the selection.
37319              * @param {Roo.form.ComboBox} combo This combo box
37320              * @param {Roo.data.Record} record The data record returned from the underlying store
37321              * @param {Number} index The index of the selected item in the dropdown list
37322              */
37323         'beforeselect' : true,
37324         /**
37325          * @event select
37326          * Fires when a list item is selected
37327              * @param {Roo.form.ComboBox} combo This combo box
37328              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37329              * @param {Number} index The index of the selected item in the dropdown list
37330              */
37331         'select' : true,
37332         /**
37333          * @event beforequery
37334          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37335          * The event object passed has these properties:
37336              * @param {Roo.form.ComboBox} combo This combo box
37337              * @param {String} query The query
37338              * @param {Boolean} forceAll true to force "all" query
37339              * @param {Boolean} cancel true to cancel the query
37340              * @param {Object} e The query event object
37341              */
37342         'beforequery': true,
37343          /**
37344          * @event add
37345          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37346              * @param {Roo.form.ComboBox} combo This combo box
37347              */
37348         'add' : true,
37349         /**
37350          * @event edit
37351          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37352              * @param {Roo.form.ComboBox} combo This combo box
37353              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37354              */
37355         'edit' : true
37356         
37357         
37358     });
37359     if(this.transform){
37360         this.allowDomMove = false;
37361         var s = Roo.getDom(this.transform);
37362         if(!this.hiddenName){
37363             this.hiddenName = s.name;
37364         }
37365         if(!this.store){
37366             this.mode = 'local';
37367             var d = [], opts = s.options;
37368             for(var i = 0, len = opts.length;i < len; i++){
37369                 var o = opts[i];
37370                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37371                 if(o.selected) {
37372                     this.value = value;
37373                 }
37374                 d.push([value, o.text]);
37375             }
37376             this.store = new Roo.data.SimpleStore({
37377                 'id': 0,
37378                 fields: ['value', 'text'],
37379                 data : d
37380             });
37381             this.valueField = 'value';
37382             this.displayField = 'text';
37383         }
37384         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37385         if(!this.lazyRender){
37386             this.target = true;
37387             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37388             s.parentNode.removeChild(s); // remove it
37389             this.render(this.el.parentNode);
37390         }else{
37391             s.parentNode.removeChild(s); // remove it
37392         }
37393
37394     }
37395     if (this.store) {
37396         this.store = Roo.factory(this.store, Roo.data);
37397     }
37398     
37399     this.selectedIndex = -1;
37400     if(this.mode == 'local'){
37401         if(config.queryDelay === undefined){
37402             this.queryDelay = 10;
37403         }
37404         if(config.minChars === undefined){
37405             this.minChars = 0;
37406         }
37407     }
37408 };
37409
37410 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37411     /**
37412      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37413      */
37414     /**
37415      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37416      * rendering into an Roo.Editor, defaults to false)
37417      */
37418     /**
37419      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37420      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37421      */
37422     /**
37423      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37424      */
37425     /**
37426      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37427      * the dropdown list (defaults to undefined, with no header element)
37428      */
37429
37430      /**
37431      * @cfg {String/Roo.Template} tpl The template to use to render the output
37432      */
37433      
37434     // private
37435     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37436     /**
37437      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37438      */
37439     listWidth: undefined,
37440     /**
37441      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37442      * mode = 'remote' or 'text' if mode = 'local')
37443      */
37444     displayField: undefined,
37445     /**
37446      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37447      * mode = 'remote' or 'value' if mode = 'local'). 
37448      * Note: use of a valueField requires the user make a selection
37449      * in order for a value to be mapped.
37450      */
37451     valueField: undefined,
37452     
37453     
37454     /**
37455      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37456      * field's data value (defaults to the underlying DOM element's name)
37457      */
37458     hiddenName: undefined,
37459     /**
37460      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37461      */
37462     listClass: '',
37463     /**
37464      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37465      */
37466     selectedClass: 'x-combo-selected',
37467     /**
37468      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37469      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37470      * which displays a downward arrow icon).
37471      */
37472     triggerClass : 'x-form-arrow-trigger',
37473     /**
37474      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37475      */
37476     shadow:'sides',
37477     /**
37478      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37479      * anchor positions (defaults to 'tl-bl')
37480      */
37481     listAlign: 'tl-bl?',
37482     /**
37483      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37484      */
37485     maxHeight: 300,
37486     /**
37487      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37488      * query specified by the allQuery config option (defaults to 'query')
37489      */
37490     triggerAction: 'query',
37491     /**
37492      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37493      * (defaults to 4, does not apply if editable = false)
37494      */
37495     minChars : 4,
37496     /**
37497      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37498      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37499      */
37500     typeAhead: false,
37501     /**
37502      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37503      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37504      */
37505     queryDelay: 500,
37506     /**
37507      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37508      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37509      */
37510     pageSize: 0,
37511     /**
37512      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37513      * when editable = true (defaults to false)
37514      */
37515     selectOnFocus:false,
37516     /**
37517      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37518      */
37519     queryParam: 'query',
37520     /**
37521      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37522      * when mode = 'remote' (defaults to 'Loading...')
37523      */
37524     loadingText: 'Loading...',
37525     /**
37526      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37527      */
37528     resizable: false,
37529     /**
37530      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37531      */
37532     handleHeight : 8,
37533     /**
37534      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37535      * traditional select (defaults to true)
37536      */
37537     editable: true,
37538     /**
37539      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37540      */
37541     allQuery: '',
37542     /**
37543      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37544      */
37545     mode: 'remote',
37546     /**
37547      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37548      * listWidth has a higher value)
37549      */
37550     minListWidth : 70,
37551     /**
37552      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37553      * allow the user to set arbitrary text into the field (defaults to false)
37554      */
37555     forceSelection:false,
37556     /**
37557      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37558      * if typeAhead = true (defaults to 250)
37559      */
37560     typeAheadDelay : 250,
37561     /**
37562      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37563      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37564      */
37565     valueNotFoundText : undefined,
37566     /**
37567      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37568      */
37569     blockFocus : false,
37570     
37571     /**
37572      * @cfg {Boolean} disableClear Disable showing of clear button.
37573      */
37574     disableClear : false,
37575     /**
37576      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37577      */
37578     alwaysQuery : false,
37579     
37580     //private
37581     addicon : false,
37582     editicon: false,
37583     
37584     // element that contains real text value.. (when hidden is used..)
37585      
37586     // private
37587     onRender : function(ct, position){
37588         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37589         if(this.hiddenName){
37590             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37591                     'before', true);
37592             this.hiddenField.value =
37593                 this.hiddenValue !== undefined ? this.hiddenValue :
37594                 this.value !== undefined ? this.value : '';
37595
37596             // prevent input submission
37597             this.el.dom.removeAttribute('name');
37598              
37599              
37600         }
37601         if(Roo.isGecko){
37602             this.el.dom.setAttribute('autocomplete', 'off');
37603         }
37604
37605         var cls = 'x-combo-list';
37606
37607         this.list = new Roo.Layer({
37608             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37609         });
37610
37611         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37612         this.list.setWidth(lw);
37613         this.list.swallowEvent('mousewheel');
37614         this.assetHeight = 0;
37615
37616         if(this.title){
37617             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37618             this.assetHeight += this.header.getHeight();
37619         }
37620
37621         this.innerList = this.list.createChild({cls:cls+'-inner'});
37622         this.innerList.on('mouseover', this.onViewOver, this);
37623         this.innerList.on('mousemove', this.onViewMove, this);
37624         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37625         
37626         if(this.allowBlank && !this.pageSize && !this.disableClear){
37627             this.footer = this.list.createChild({cls:cls+'-ft'});
37628             this.pageTb = new Roo.Toolbar(this.footer);
37629            
37630         }
37631         if(this.pageSize){
37632             this.footer = this.list.createChild({cls:cls+'-ft'});
37633             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37634                     {pageSize: this.pageSize});
37635             
37636         }
37637         
37638         if (this.pageTb && this.allowBlank && !this.disableClear) {
37639             var _this = this;
37640             this.pageTb.add(new Roo.Toolbar.Fill(), {
37641                 cls: 'x-btn-icon x-btn-clear',
37642                 text: '&#160;',
37643                 handler: function()
37644                 {
37645                     _this.collapse();
37646                     _this.clearValue();
37647                     _this.onSelect(false, -1);
37648                 }
37649             });
37650         }
37651         if (this.footer) {
37652             this.assetHeight += this.footer.getHeight();
37653         }
37654         
37655
37656         if(!this.tpl){
37657             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37658         }
37659
37660         this.view = new Roo.View(this.innerList, this.tpl, {
37661             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37662         });
37663
37664         this.view.on('click', this.onViewClick, this);
37665
37666         this.store.on('beforeload', this.onBeforeLoad, this);
37667         this.store.on('load', this.onLoad, this);
37668         this.store.on('loadexception', this.onLoadException, this);
37669
37670         if(this.resizable){
37671             this.resizer = new Roo.Resizable(this.list,  {
37672                pinned:true, handles:'se'
37673             });
37674             this.resizer.on('resize', function(r, w, h){
37675                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37676                 this.listWidth = w;
37677                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37678                 this.restrictHeight();
37679             }, this);
37680             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37681         }
37682         if(!this.editable){
37683             this.editable = true;
37684             this.setEditable(false);
37685         }  
37686         
37687         
37688         if (typeof(this.events.add.listeners) != 'undefined') {
37689             
37690             this.addicon = this.wrap.createChild(
37691                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37692        
37693             this.addicon.on('click', function(e) {
37694                 this.fireEvent('add', this);
37695             }, this);
37696         }
37697         if (typeof(this.events.edit.listeners) != 'undefined') {
37698             
37699             this.editicon = this.wrap.createChild(
37700                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37701             if (this.addicon) {
37702                 this.editicon.setStyle('margin-left', '40px');
37703             }
37704             this.editicon.on('click', function(e) {
37705                 
37706                 // we fire even  if inothing is selected..
37707                 this.fireEvent('edit', this, this.lastData );
37708                 
37709             }, this);
37710         }
37711         
37712         
37713         
37714     },
37715
37716     // private
37717     initEvents : function(){
37718         Roo.form.ComboBox.superclass.initEvents.call(this);
37719
37720         this.keyNav = new Roo.KeyNav(this.el, {
37721             "up" : function(e){
37722                 this.inKeyMode = true;
37723                 this.selectPrev();
37724             },
37725
37726             "down" : function(e){
37727                 if(!this.isExpanded()){
37728                     this.onTriggerClick();
37729                 }else{
37730                     this.inKeyMode = true;
37731                     this.selectNext();
37732                 }
37733             },
37734
37735             "enter" : function(e){
37736                 this.onViewClick();
37737                 //return true;
37738             },
37739
37740             "esc" : function(e){
37741                 this.collapse();
37742             },
37743
37744             "tab" : function(e){
37745                 this.onViewClick(false);
37746                 this.fireEvent("specialkey", this, e);
37747                 return true;
37748             },
37749
37750             scope : this,
37751
37752             doRelay : function(foo, bar, hname){
37753                 if(hname == 'down' || this.scope.isExpanded()){
37754                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37755                 }
37756                 return true;
37757             },
37758
37759             forceKeyDown: true
37760         });
37761         this.queryDelay = Math.max(this.queryDelay || 10,
37762                 this.mode == 'local' ? 10 : 250);
37763         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37764         if(this.typeAhead){
37765             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37766         }
37767         if(this.editable !== false){
37768             this.el.on("keyup", this.onKeyUp, this);
37769         }
37770         if(this.forceSelection){
37771             this.on('blur', this.doForce, this);
37772         }
37773     },
37774
37775     onDestroy : function(){
37776         if(this.view){
37777             this.view.setStore(null);
37778             this.view.el.removeAllListeners();
37779             this.view.el.remove();
37780             this.view.purgeListeners();
37781         }
37782         if(this.list){
37783             this.list.destroy();
37784         }
37785         if(this.store){
37786             this.store.un('beforeload', this.onBeforeLoad, this);
37787             this.store.un('load', this.onLoad, this);
37788             this.store.un('loadexception', this.onLoadException, this);
37789         }
37790         Roo.form.ComboBox.superclass.onDestroy.call(this);
37791     },
37792
37793     // private
37794     fireKey : function(e){
37795         if(e.isNavKeyPress() && !this.list.isVisible()){
37796             this.fireEvent("specialkey", this, e);
37797         }
37798     },
37799
37800     // private
37801     onResize: function(w, h){
37802         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37803         
37804         if(typeof w != 'number'){
37805             // we do not handle it!?!?
37806             return;
37807         }
37808         var tw = this.trigger.getWidth();
37809         tw += this.addicon ? this.addicon.getWidth() : 0;
37810         tw += this.editicon ? this.editicon.getWidth() : 0;
37811         var x = w - tw;
37812         this.el.setWidth( this.adjustWidth('input', x));
37813             
37814         this.trigger.setStyle('left', x+'px');
37815         
37816         if(this.list && this.listWidth === undefined){
37817             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37818             this.list.setWidth(lw);
37819             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37820         }
37821         
37822     
37823         
37824     },
37825
37826     /**
37827      * Allow or prevent the user from directly editing the field text.  If false is passed,
37828      * the user will only be able to select from the items defined in the dropdown list.  This method
37829      * is the runtime equivalent of setting the 'editable' config option at config time.
37830      * @param {Boolean} value True to allow the user to directly edit the field text
37831      */
37832     setEditable : function(value){
37833         if(value == this.editable){
37834             return;
37835         }
37836         this.editable = value;
37837         if(!value){
37838             this.el.dom.setAttribute('readOnly', true);
37839             this.el.on('mousedown', this.onTriggerClick,  this);
37840             this.el.addClass('x-combo-noedit');
37841         }else{
37842             this.el.dom.setAttribute('readOnly', false);
37843             this.el.un('mousedown', this.onTriggerClick,  this);
37844             this.el.removeClass('x-combo-noedit');
37845         }
37846     },
37847
37848     // private
37849     onBeforeLoad : function(){
37850         if(!this.hasFocus){
37851             return;
37852         }
37853         this.innerList.update(this.loadingText ?
37854                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37855         this.restrictHeight();
37856         this.selectedIndex = -1;
37857     },
37858
37859     // private
37860     onLoad : function(){
37861         if(!this.hasFocus){
37862             return;
37863         }
37864         if(this.store.getCount() > 0){
37865             this.expand();
37866             this.restrictHeight();
37867             if(this.lastQuery == this.allQuery){
37868                 if(this.editable){
37869                     this.el.dom.select();
37870                 }
37871                 if(!this.selectByValue(this.value, true)){
37872                     this.select(0, true);
37873                 }
37874             }else{
37875                 this.selectNext();
37876                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37877                     this.taTask.delay(this.typeAheadDelay);
37878                 }
37879             }
37880         }else{
37881             this.onEmptyResults();
37882         }
37883         //this.el.focus();
37884     },
37885     // private
37886     onLoadException : function()
37887     {
37888         this.collapse();
37889         Roo.log(this.store.reader.jsonData);
37890         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37891             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37892         }
37893         
37894         
37895     },
37896     // private
37897     onTypeAhead : function(){
37898         if(this.store.getCount() > 0){
37899             var r = this.store.getAt(0);
37900             var newValue = r.data[this.displayField];
37901             var len = newValue.length;
37902             var selStart = this.getRawValue().length;
37903             if(selStart != len){
37904                 this.setRawValue(newValue);
37905                 this.selectText(selStart, newValue.length);
37906             }
37907         }
37908     },
37909
37910     // private
37911     onSelect : function(record, index){
37912         if(this.fireEvent('beforeselect', this, record, index) !== false){
37913             this.setFromData(index > -1 ? record.data : false);
37914             this.collapse();
37915             this.fireEvent('select', this, record, index);
37916         }
37917     },
37918
37919     /**
37920      * Returns the currently selected field value or empty string if no value is set.
37921      * @return {String} value The selected value
37922      */
37923     getValue : function(){
37924         if(this.valueField){
37925             return typeof this.value != 'undefined' ? this.value : '';
37926         }else{
37927             return Roo.form.ComboBox.superclass.getValue.call(this);
37928         }
37929     },
37930
37931     /**
37932      * Clears any text/value currently set in the field
37933      */
37934     clearValue : function(){
37935         if(this.hiddenField){
37936             this.hiddenField.value = '';
37937         }
37938         this.value = '';
37939         this.setRawValue('');
37940         this.lastSelectionText = '';
37941         this.applyEmptyText();
37942     },
37943
37944     /**
37945      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37946      * will be displayed in the field.  If the value does not match the data value of an existing item,
37947      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37948      * Otherwise the field will be blank (although the value will still be set).
37949      * @param {String} value The value to match
37950      */
37951     setValue : function(v){
37952         var text = v;
37953         if(this.valueField){
37954             var r = this.findRecord(this.valueField, v);
37955             if(r){
37956                 text = r.data[this.displayField];
37957             }else if(this.valueNotFoundText !== undefined){
37958                 text = this.valueNotFoundText;
37959             }
37960         }
37961         this.lastSelectionText = text;
37962         if(this.hiddenField){
37963             this.hiddenField.value = v;
37964         }
37965         Roo.form.ComboBox.superclass.setValue.call(this, text);
37966         this.value = v;
37967     },
37968     /**
37969      * @property {Object} the last set data for the element
37970      */
37971     
37972     lastData : false,
37973     /**
37974      * Sets the value of the field based on a object which is related to the record format for the store.
37975      * @param {Object} value the value to set as. or false on reset?
37976      */
37977     setFromData : function(o){
37978         var dv = ''; // display value
37979         var vv = ''; // value value..
37980         this.lastData = o;
37981         if (this.displayField) {
37982             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37983         } else {
37984             // this is an error condition!!!
37985             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37986         }
37987         
37988         if(this.valueField){
37989             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37990         }
37991         if(this.hiddenField){
37992             this.hiddenField.value = vv;
37993             
37994             this.lastSelectionText = dv;
37995             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37996             this.value = vv;
37997             return;
37998         }
37999         // no hidden field.. - we store the value in 'value', but still display
38000         // display field!!!!
38001         this.lastSelectionText = dv;
38002         Roo.form.ComboBox.superclass.setValue.call(this, dv);
38003         this.value = vv;
38004         
38005         
38006     },
38007     // private
38008     reset : function(){
38009         // overridden so that last data is reset..
38010         this.setValue(this.originalValue);
38011         this.clearInvalid();
38012         this.lastData = false;
38013     },
38014     // private
38015     findRecord : function(prop, value){
38016         var record;
38017         if(this.store.getCount() > 0){
38018             this.store.each(function(r){
38019                 if(r.data[prop] == value){
38020                     record = r;
38021                     return false;
38022                 }
38023                 return true;
38024             });
38025         }
38026         return record;
38027     },
38028     
38029     getName: function()
38030     {
38031         // returns hidden if it's set..
38032         if (!this.rendered) {return ''};
38033         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38034         
38035     },
38036     // private
38037     onViewMove : function(e, t){
38038         this.inKeyMode = false;
38039     },
38040
38041     // private
38042     onViewOver : function(e, t){
38043         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38044             return;
38045         }
38046         var item = this.view.findItemFromChild(t);
38047         if(item){
38048             var index = this.view.indexOf(item);
38049             this.select(index, false);
38050         }
38051     },
38052
38053     // private
38054     onViewClick : function(doFocus)
38055     {
38056         var index = this.view.getSelectedIndexes()[0];
38057         var r = this.store.getAt(index);
38058         if(r){
38059             this.onSelect(r, index);
38060         }
38061         if(doFocus !== false && !this.blockFocus){
38062             this.el.focus();
38063         }
38064     },
38065
38066     // private
38067     restrictHeight : function(){
38068         this.innerList.dom.style.height = '';
38069         var inner = this.innerList.dom;
38070         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38071         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38072         this.list.beginUpdate();
38073         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38074         this.list.alignTo(this.el, this.listAlign);
38075         this.list.endUpdate();
38076     },
38077
38078     // private
38079     onEmptyResults : function(){
38080         this.collapse();
38081     },
38082
38083     /**
38084      * Returns true if the dropdown list is expanded, else false.
38085      */
38086     isExpanded : function(){
38087         return this.list.isVisible();
38088     },
38089
38090     /**
38091      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38092      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38093      * @param {String} value The data value of the item to select
38094      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38095      * selected item if it is not currently in view (defaults to true)
38096      * @return {Boolean} True if the value matched an item in the list, else false
38097      */
38098     selectByValue : function(v, scrollIntoView){
38099         if(v !== undefined && v !== null){
38100             var r = this.findRecord(this.valueField || this.displayField, v);
38101             if(r){
38102                 this.select(this.store.indexOf(r), scrollIntoView);
38103                 return true;
38104             }
38105         }
38106         return false;
38107     },
38108
38109     /**
38110      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38111      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38112      * @param {Number} index The zero-based index of the list item to select
38113      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38114      * selected item if it is not currently in view (defaults to true)
38115      */
38116     select : function(index, scrollIntoView){
38117         this.selectedIndex = index;
38118         this.view.select(index);
38119         if(scrollIntoView !== false){
38120             var el = this.view.getNode(index);
38121             if(el){
38122                 this.innerList.scrollChildIntoView(el, false);
38123             }
38124         }
38125     },
38126
38127     // private
38128     selectNext : function(){
38129         var ct = this.store.getCount();
38130         if(ct > 0){
38131             if(this.selectedIndex == -1){
38132                 this.select(0);
38133             }else if(this.selectedIndex < ct-1){
38134                 this.select(this.selectedIndex+1);
38135             }
38136         }
38137     },
38138
38139     // private
38140     selectPrev : function(){
38141         var ct = this.store.getCount();
38142         if(ct > 0){
38143             if(this.selectedIndex == -1){
38144                 this.select(0);
38145             }else if(this.selectedIndex != 0){
38146                 this.select(this.selectedIndex-1);
38147             }
38148         }
38149     },
38150
38151     // private
38152     onKeyUp : function(e){
38153         if(this.editable !== false && !e.isSpecialKey()){
38154             this.lastKey = e.getKey();
38155             this.dqTask.delay(this.queryDelay);
38156         }
38157     },
38158
38159     // private
38160     validateBlur : function(){
38161         return !this.list || !this.list.isVisible();   
38162     },
38163
38164     // private
38165     initQuery : function(){
38166         this.doQuery(this.getRawValue());
38167     },
38168
38169     // private
38170     doForce : function(){
38171         if(this.el.dom.value.length > 0){
38172             this.el.dom.value =
38173                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38174             this.applyEmptyText();
38175         }
38176     },
38177
38178     /**
38179      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38180      * query allowing the query action to be canceled if needed.
38181      * @param {String} query The SQL query to execute
38182      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38183      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38184      * saved in the current store (defaults to false)
38185      */
38186     doQuery : function(q, forceAll){
38187         if(q === undefined || q === null){
38188             q = '';
38189         }
38190         var qe = {
38191             query: q,
38192             forceAll: forceAll,
38193             combo: this,
38194             cancel:false
38195         };
38196         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38197             return false;
38198         }
38199         q = qe.query;
38200         forceAll = qe.forceAll;
38201         if(forceAll === true || (q.length >= this.minChars)){
38202             if(this.lastQuery != q || this.alwaysQuery){
38203                 this.lastQuery = q;
38204                 if(this.mode == 'local'){
38205                     this.selectedIndex = -1;
38206                     if(forceAll){
38207                         this.store.clearFilter();
38208                     }else{
38209                         this.store.filter(this.displayField, q);
38210                     }
38211                     this.onLoad();
38212                 }else{
38213                     this.store.baseParams[this.queryParam] = q;
38214                     this.store.load({
38215                         params: this.getParams(q)
38216                     });
38217                     this.expand();
38218                 }
38219             }else{
38220                 this.selectedIndex = -1;
38221                 this.onLoad();   
38222             }
38223         }
38224     },
38225
38226     // private
38227     getParams : function(q){
38228         var p = {};
38229         //p[this.queryParam] = q;
38230         if(this.pageSize){
38231             p.start = 0;
38232             p.limit = this.pageSize;
38233         }
38234         return p;
38235     },
38236
38237     /**
38238      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38239      */
38240     collapse : function(){
38241         if(!this.isExpanded()){
38242             return;
38243         }
38244         this.list.hide();
38245         Roo.get(document).un('mousedown', this.collapseIf, this);
38246         Roo.get(document).un('mousewheel', this.collapseIf, this);
38247         if (!this.editable) {
38248             Roo.get(document).un('keydown', this.listKeyPress, this);
38249         }
38250         this.fireEvent('collapse', this);
38251     },
38252
38253     // private
38254     collapseIf : function(e){
38255         if(!e.within(this.wrap) && !e.within(this.list)){
38256             this.collapse();
38257         }
38258     },
38259
38260     /**
38261      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38262      */
38263     expand : function(){
38264         if(this.isExpanded() || !this.hasFocus){
38265             return;
38266         }
38267         this.list.alignTo(this.el, this.listAlign);
38268         this.list.show();
38269         Roo.get(document).on('mousedown', this.collapseIf, this);
38270         Roo.get(document).on('mousewheel', this.collapseIf, this);
38271         if (!this.editable) {
38272             Roo.get(document).on('keydown', this.listKeyPress, this);
38273         }
38274         
38275         this.fireEvent('expand', this);
38276     },
38277
38278     // private
38279     // Implements the default empty TriggerField.onTriggerClick function
38280     onTriggerClick : function(){
38281         if(this.disabled){
38282             return;
38283         }
38284         if(this.isExpanded()){
38285             this.collapse();
38286             if (!this.blockFocus) {
38287                 this.el.focus();
38288             }
38289             
38290         }else {
38291             this.hasFocus = true;
38292             if(this.triggerAction == 'all') {
38293                 this.doQuery(this.allQuery, true);
38294             } else {
38295                 this.doQuery(this.getRawValue());
38296             }
38297             if (!this.blockFocus) {
38298                 this.el.focus();
38299             }
38300         }
38301     },
38302     listKeyPress : function(e)
38303     {
38304         //Roo.log('listkeypress');
38305         // scroll to first matching element based on key pres..
38306         if (e.isSpecialKey()) {
38307             return false;
38308         }
38309         var k = String.fromCharCode(e.getKey()).toUpperCase();
38310         //Roo.log(k);
38311         var match  = false;
38312         var csel = this.view.getSelectedNodes();
38313         var cselitem = false;
38314         if (csel.length) {
38315             var ix = this.view.indexOf(csel[0]);
38316             cselitem  = this.store.getAt(ix);
38317             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38318                 cselitem = false;
38319             }
38320             
38321         }
38322         
38323         this.store.each(function(v) { 
38324             if (cselitem) {
38325                 // start at existing selection.
38326                 if (cselitem.id == v.id) {
38327                     cselitem = false;
38328                 }
38329                 return;
38330             }
38331                 
38332             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38333                 match = this.store.indexOf(v);
38334                 return false;
38335             }
38336         }, this);
38337         
38338         if (match === false) {
38339             return true; // no more action?
38340         }
38341         // scroll to?
38342         this.view.select(match);
38343         var sn = Roo.get(this.view.getSelectedNodes()[0])
38344         sn.scrollIntoView(sn.dom.parentNode, false);
38345     }
38346
38347     /** 
38348     * @cfg {Boolean} grow 
38349     * @hide 
38350     */
38351     /** 
38352     * @cfg {Number} growMin 
38353     * @hide 
38354     */
38355     /** 
38356     * @cfg {Number} growMax 
38357     * @hide 
38358     */
38359     /**
38360      * @hide
38361      * @method autoSize
38362      */
38363 });/*
38364  * Based on:
38365  * Ext JS Library 1.1.1
38366  * Copyright(c) 2006-2007, Ext JS, LLC.
38367  *
38368  * Originally Released Under LGPL - original licence link has changed is not relivant.
38369  *
38370  * Fork - LGPL
38371  * <script type="text/javascript">
38372  */
38373 /**
38374  * @class Roo.form.Checkbox
38375  * @extends Roo.form.Field
38376  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38377  * @constructor
38378  * Creates a new Checkbox
38379  * @param {Object} config Configuration options
38380  */
38381 Roo.form.Checkbox = function(config){
38382     Roo.form.Checkbox.superclass.constructor.call(this, config);
38383     this.addEvents({
38384         /**
38385          * @event check
38386          * Fires when the checkbox is checked or unchecked.
38387              * @param {Roo.form.Checkbox} this This checkbox
38388              * @param {Boolean} checked The new checked value
38389              */
38390         check : true
38391     });
38392 };
38393
38394 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38395     /**
38396      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38397      */
38398     focusClass : undefined,
38399     /**
38400      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38401      */
38402     fieldClass: "x-form-field",
38403     /**
38404      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38405      */
38406     checked: false,
38407     /**
38408      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38409      * {tag: "input", type: "checkbox", autocomplete: "off"})
38410      */
38411     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38412     /**
38413      * @cfg {String} boxLabel The text that appears beside the checkbox
38414      */
38415     boxLabel : "",
38416     /**
38417      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38418      */  
38419     inputValue : '1',
38420     /**
38421      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38422      */
38423      valueOff: '0', // value when not checked..
38424
38425     actionMode : 'viewEl', 
38426     //
38427     // private
38428     itemCls : 'x-menu-check-item x-form-item',
38429     groupClass : 'x-menu-group-item',
38430     inputType : 'hidden',
38431     
38432     
38433     inSetChecked: false, // check that we are not calling self...
38434     
38435     inputElement: false, // real input element?
38436     basedOn: false, // ????
38437     
38438     isFormField: true, // not sure where this is needed!!!!
38439
38440     onResize : function(){
38441         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38442         if(!this.boxLabel){
38443             this.el.alignTo(this.wrap, 'c-c');
38444         }
38445     },
38446
38447     initEvents : function(){
38448         Roo.form.Checkbox.superclass.initEvents.call(this);
38449         this.el.on("click", this.onClick,  this);
38450         this.el.on("change", this.onClick,  this);
38451     },
38452
38453
38454     getResizeEl : function(){
38455         return this.wrap;
38456     },
38457
38458     getPositionEl : function(){
38459         return this.wrap;
38460     },
38461
38462     // private
38463     onRender : function(ct, position){
38464         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38465         /*
38466         if(this.inputValue !== undefined){
38467             this.el.dom.value = this.inputValue;
38468         }
38469         */
38470         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38471         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38472         var viewEl = this.wrap.createChild({ 
38473             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38474         this.viewEl = viewEl;   
38475         this.wrap.on('click', this.onClick,  this); 
38476         
38477         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38478         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38479         
38480         
38481         
38482         if(this.boxLabel){
38483             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38484         //    viewEl.on('click', this.onClick,  this); 
38485         }
38486         //if(this.checked){
38487             this.setChecked(this.checked);
38488         //}else{
38489             //this.checked = this.el.dom;
38490         //}
38491
38492     },
38493
38494     // private
38495     initValue : Roo.emptyFn,
38496
38497     /**
38498      * Returns the checked state of the checkbox.
38499      * @return {Boolean} True if checked, else false
38500      */
38501     getValue : function(){
38502         if(this.el){
38503             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38504         }
38505         return this.valueOff;
38506         
38507     },
38508
38509         // private
38510     onClick : function(){ 
38511         this.setChecked(!this.checked);
38512
38513         //if(this.el.dom.checked != this.checked){
38514         //    this.setValue(this.el.dom.checked);
38515        // }
38516     },
38517
38518     /**
38519      * Sets the checked state of the checkbox.
38520      * On is always based on a string comparison between inputValue and the param.
38521      * @param {Boolean/String} value - the value to set 
38522      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38523      */
38524     setValue : function(v,suppressEvent){
38525         
38526         
38527         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38528         //if(this.el && this.el.dom){
38529         //    this.el.dom.checked = this.checked;
38530         //    this.el.dom.defaultChecked = this.checked;
38531         //}
38532         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38533         //this.fireEvent("check", this, this.checked);
38534     },
38535     // private..
38536     setChecked : function(state,suppressEvent)
38537     {
38538         if (this.inSetChecked) {
38539             this.checked = state;
38540             return;
38541         }
38542         
38543     
38544         if(this.wrap){
38545             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38546         }
38547         this.checked = state;
38548         if(suppressEvent !== true){
38549             this.fireEvent('check', this, state);
38550         }
38551         this.inSetChecked = true;
38552         this.el.dom.value = state ? this.inputValue : this.valueOff;
38553         this.inSetChecked = false;
38554         
38555     },
38556     // handle setting of hidden value by some other method!!?!?
38557     setFromHidden: function()
38558     {
38559         if(!this.el){
38560             return;
38561         }
38562         //console.log("SET FROM HIDDEN");
38563         //alert('setFrom hidden');
38564         this.setValue(this.el.dom.value);
38565     },
38566     
38567     onDestroy : function()
38568     {
38569         if(this.viewEl){
38570             Roo.get(this.viewEl).remove();
38571         }
38572          
38573         Roo.form.Checkbox.superclass.onDestroy.call(this);
38574     }
38575
38576 });/*
38577  * Based on:
38578  * Ext JS Library 1.1.1
38579  * Copyright(c) 2006-2007, Ext JS, LLC.
38580  *
38581  * Originally Released Under LGPL - original licence link has changed is not relivant.
38582  *
38583  * Fork - LGPL
38584  * <script type="text/javascript">
38585  */
38586  
38587 /**
38588  * @class Roo.form.Radio
38589  * @extends Roo.form.Checkbox
38590  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38591  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38592  * @constructor
38593  * Creates a new Radio
38594  * @param {Object} config Configuration options
38595  */
38596 Roo.form.Radio = function(){
38597     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38598 };
38599 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38600     inputType: 'radio',
38601
38602     /**
38603      * If this radio is part of a group, it will return the selected value
38604      * @return {String}
38605      */
38606     getGroupValue : function(){
38607         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38608     }
38609 });//<script type="text/javascript">
38610
38611 /*
38612  * Ext JS Library 1.1.1
38613  * Copyright(c) 2006-2007, Ext JS, LLC.
38614  * licensing@extjs.com
38615  * 
38616  * http://www.extjs.com/license
38617  */
38618  
38619  /*
38620   * 
38621   * Known bugs:
38622   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38623   * - IE ? - no idea how much works there.
38624   * 
38625   * 
38626   * 
38627   */
38628  
38629
38630 /**
38631  * @class Ext.form.HtmlEditor
38632  * @extends Ext.form.Field
38633  * Provides a lightweight HTML Editor component.
38634  *
38635  * This has been tested on Fireforx / Chrome.. IE may not be so great..
38636  * 
38637  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38638  * supported by this editor.</b><br/><br/>
38639  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38640  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38641  */
38642 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38643       /**
38644      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38645      */
38646     toolbars : false,
38647     /**
38648      * @cfg {String} createLinkText The default text for the create link prompt
38649      */
38650     createLinkText : 'Please enter the URL for the link:',
38651     /**
38652      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38653      */
38654     defaultLinkValue : 'http:/'+'/',
38655    
38656      /**
38657      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38658      *                        Roo.resizable.
38659      */
38660     resizable : false,
38661      /**
38662      * @cfg {Number} height (in pixels)
38663      */   
38664     height: 300,
38665    /**
38666      * @cfg {Number} width (in pixels)
38667      */   
38668     width: 500,
38669     
38670     /**
38671      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38672      * 
38673      */
38674     stylesheets: false,
38675     
38676     // id of frame..
38677     frameId: false,
38678     
38679     // private properties
38680     validationEvent : false,
38681     deferHeight: true,
38682     initialized : false,
38683     activated : false,
38684     sourceEditMode : false,
38685     onFocus : Roo.emptyFn,
38686     iframePad:3,
38687     hideMode:'offsets',
38688     
38689     defaultAutoCreate : { // modified by initCompnoent..
38690         tag: "textarea",
38691         style:"width:500px;height:300px;",
38692         autocomplete: "off"
38693     },
38694
38695     // private
38696     initComponent : function(){
38697         this.addEvents({
38698             /**
38699              * @event initialize
38700              * Fires when the editor is fully initialized (including the iframe)
38701              * @param {HtmlEditor} this
38702              */
38703             initialize: true,
38704             /**
38705              * @event activate
38706              * Fires when the editor is first receives the focus. Any insertion must wait
38707              * until after this event.
38708              * @param {HtmlEditor} this
38709              */
38710             activate: true,
38711              /**
38712              * @event beforesync
38713              * Fires before the textarea is updated with content from the editor iframe. Return false
38714              * to cancel the sync.
38715              * @param {HtmlEditor} this
38716              * @param {String} html
38717              */
38718             beforesync: true,
38719              /**
38720              * @event beforepush
38721              * Fires before the iframe editor is updated with content from the textarea. Return false
38722              * to cancel the push.
38723              * @param {HtmlEditor} this
38724              * @param {String} html
38725              */
38726             beforepush: true,
38727              /**
38728              * @event sync
38729              * Fires when the textarea is updated with content from the editor iframe.
38730              * @param {HtmlEditor} this
38731              * @param {String} html
38732              */
38733             sync: true,
38734              /**
38735              * @event push
38736              * Fires when the iframe editor is updated with content from the textarea.
38737              * @param {HtmlEditor} this
38738              * @param {String} html
38739              */
38740             push: true,
38741              /**
38742              * @event editmodechange
38743              * Fires when the editor switches edit modes
38744              * @param {HtmlEditor} this
38745              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38746              */
38747             editmodechange: true,
38748             /**
38749              * @event editorevent
38750              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38751              * @param {HtmlEditor} this
38752              */
38753             editorevent: true
38754         });
38755         this.defaultAutoCreate =  {
38756             tag: "textarea",
38757             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38758             autocomplete: "off"
38759         };
38760     },
38761
38762     /**
38763      * Protected method that will not generally be called directly. It
38764      * is called when the editor creates its toolbar. Override this method if you need to
38765      * add custom toolbar buttons.
38766      * @param {HtmlEditor} editor
38767      */
38768     createToolbar : function(editor){
38769         if (!editor.toolbars || !editor.toolbars.length) {
38770             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38771         }
38772         
38773         for (var i =0 ; i < editor.toolbars.length;i++) {
38774             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38775             editor.toolbars[i].init(editor);
38776         }
38777          
38778         
38779     },
38780
38781     /**
38782      * Protected method that will not generally be called directly. It
38783      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38784      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38785      */
38786     getDocMarkup : function(){
38787         // body styles..
38788         var st = '';
38789         if (this.stylesheets === false) {
38790             
38791             Roo.get(document.head).select('style').each(function(node) {
38792                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38793             });
38794             
38795             Roo.get(document.head).select('link').each(function(node) { 
38796                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38797             });
38798             
38799         } else if (!this.stylesheets.length) {
38800                 // simple..
38801                 st = '<style type="text/css">' +
38802                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38803                    '</style>';
38804         } else {
38805             Roo.each(this.stylesheets, function(s) {
38806                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38807             });
38808             
38809         }
38810         
38811         st +=  '<style type="text/css">' +
38812             'IMG { cursor: pointer } ' +
38813         '</style>';
38814
38815         
38816         return '<html><head>' + st  +
38817             //<style type="text/css">' +
38818             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38819             //'</style>' +
38820             ' </head><body></body></html>';
38821     },
38822
38823     // private
38824     onRender : function(ct, position)
38825     {
38826         var _t = this;
38827         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38828         this.el.dom.style.border = '0 none';
38829         this.el.dom.setAttribute('tabIndex', -1);
38830         this.el.addClass('x-hidden');
38831         if(Roo.isIE){ // fix IE 1px bogus margin
38832             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38833         }
38834         this.wrap = this.el.wrap({
38835             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38836         });
38837         
38838         if (this.resizable) {
38839             this.resizeEl = new Roo.Resizable(this.wrap, {
38840                 pinned : true,
38841                 wrap: true,
38842                 dynamic : true,
38843                 minHeight : this.height,
38844                 height: this.height,
38845                 handles : this.resizable,
38846                 width: this.width,
38847                 listeners : {
38848                     resize : function(r, w, h) {
38849                         _t.onResize(w,h); // -something
38850                     }
38851                 }
38852             });
38853             
38854         }
38855
38856         this.frameId = Roo.id();
38857         
38858         this.createToolbar(this);
38859         
38860       
38861         
38862         var iframe = this.wrap.createChild({
38863             tag: 'iframe',
38864             id: this.frameId,
38865             name: this.frameId,
38866             frameBorder : 'no',
38867             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38868         }, this.el
38869         );
38870         
38871        // console.log(iframe);
38872         //this.wrap.dom.appendChild(iframe);
38873
38874         this.iframe = iframe.dom;
38875
38876          this.assignDocWin();
38877         
38878         this.doc.designMode = 'on';
38879        
38880         this.doc.open();
38881         this.doc.write(this.getDocMarkup());
38882         this.doc.close();
38883
38884         
38885         var task = { // must defer to wait for browser to be ready
38886             run : function(){
38887                 //console.log("run task?" + this.doc.readyState);
38888                 this.assignDocWin();
38889                 if(this.doc.body || this.doc.readyState == 'complete'){
38890                     try {
38891                         this.doc.designMode="on";
38892                     } catch (e) {
38893                         return;
38894                     }
38895                     Roo.TaskMgr.stop(task);
38896                     this.initEditor.defer(10, this);
38897                 }
38898             },
38899             interval : 10,
38900             duration:10000,
38901             scope: this
38902         };
38903         Roo.TaskMgr.start(task);
38904
38905         if(!this.width){
38906             this.setSize(this.wrap.getSize());
38907         }
38908         if (this.resizeEl) {
38909             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38910             // should trigger onReize..
38911         }
38912     },
38913
38914     // private
38915     onResize : function(w, h)
38916     {
38917         //Roo.log('resize: ' +w + ',' + h );
38918         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38919         if(this.el && this.iframe){
38920             if(typeof w == 'number'){
38921                 var aw = w - this.wrap.getFrameWidth('lr');
38922                 this.el.setWidth(this.adjustWidth('textarea', aw));
38923                 this.iframe.style.width = aw + 'px';
38924             }
38925             if(typeof h == 'number'){
38926                 var tbh = 0;
38927                 for (var i =0; i < this.toolbars.length;i++) {
38928                     // fixme - ask toolbars for heights?
38929                     tbh += this.toolbars[i].tb.el.getHeight();
38930                     if (this.toolbars[i].footer) {
38931                         tbh += this.toolbars[i].footer.el.getHeight();
38932                     }
38933                 }
38934                 
38935                 
38936                 
38937                 
38938                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38939                 ah -= 5; // knock a few pixes off for look..
38940                 this.el.setHeight(this.adjustWidth('textarea', ah));
38941                 this.iframe.style.height = ah + 'px';
38942                 if(this.doc){
38943                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38944                 }
38945             }
38946         }
38947     },
38948
38949     /**
38950      * Toggles the editor between standard and source edit mode.
38951      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38952      */
38953     toggleSourceEdit : function(sourceEditMode){
38954         
38955         this.sourceEditMode = sourceEditMode === true;
38956         
38957         if(this.sourceEditMode){
38958           
38959             this.syncValue();
38960             this.iframe.className = 'x-hidden';
38961             this.el.removeClass('x-hidden');
38962             this.el.dom.removeAttribute('tabIndex');
38963             this.el.focus();
38964         }else{
38965              
38966             this.pushValue();
38967             this.iframe.className = '';
38968             this.el.addClass('x-hidden');
38969             this.el.dom.setAttribute('tabIndex', -1);
38970             this.deferFocus();
38971         }
38972         this.setSize(this.wrap.getSize());
38973         this.fireEvent('editmodechange', this, this.sourceEditMode);
38974     },
38975
38976     // private used internally
38977     createLink : function(){
38978         var url = prompt(this.createLinkText, this.defaultLinkValue);
38979         if(url && url != 'http:/'+'/'){
38980             this.relayCmd('createlink', url);
38981         }
38982     },
38983
38984     // private (for BoxComponent)
38985     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38986
38987     // private (for BoxComponent)
38988     getResizeEl : function(){
38989         return this.wrap;
38990     },
38991
38992     // private (for BoxComponent)
38993     getPositionEl : function(){
38994         return this.wrap;
38995     },
38996
38997     // private
38998     initEvents : function(){
38999         this.originalValue = this.getValue();
39000     },
39001
39002     /**
39003      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39004      * @method
39005      */
39006     markInvalid : Roo.emptyFn,
39007     /**
39008      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39009      * @method
39010      */
39011     clearInvalid : Roo.emptyFn,
39012
39013     setValue : function(v){
39014         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
39015         this.pushValue();
39016     },
39017
39018     /**
39019      * Protected method that will not generally be called directly. If you need/want
39020      * custom HTML cleanup, this is the method you should override.
39021      * @param {String} html The HTML to be cleaned
39022      * return {String} The cleaned HTML
39023      */
39024     cleanHtml : function(html){
39025         html = String(html);
39026         if(html.length > 5){
39027             if(Roo.isSafari){ // strip safari nonsense
39028                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39029             }
39030         }
39031         if(html == '&nbsp;'){
39032             html = '';
39033         }
39034         return html;
39035     },
39036
39037     /**
39038      * Protected method that will not generally be called directly. Syncs the contents
39039      * of the editor iframe with the textarea.
39040      */
39041     syncValue : function(){
39042         if(this.initialized){
39043             var bd = (this.doc.body || this.doc.documentElement);
39044             //this.cleanUpPaste(); -- this is done else where and causes havoc..
39045             var html = bd.innerHTML;
39046             if(Roo.isSafari){
39047                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39048                 var m = bs.match(/text-align:(.*?);/i);
39049                 if(m && m[1]){
39050                     html = '<div style="'+m[0]+'">' + html + '</div>';
39051                 }
39052             }
39053             html = this.cleanHtml(html);
39054             // fix up the special chars..
39055             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
39056                 return "&#"+b.charCodeAt()+";" 
39057             });
39058             if(this.fireEvent('beforesync', this, html) !== false){
39059                 this.el.dom.value = html;
39060                 this.fireEvent('sync', this, html);
39061             }
39062         }
39063     },
39064
39065     /**
39066      * Protected method that will not generally be called directly. Pushes the value of the textarea
39067      * into the iframe editor.
39068      */
39069     pushValue : function(){
39070         if(this.initialized){
39071             var v = this.el.dom.value;
39072             if(v.length < 1){
39073                 v = '&#160;';
39074             }
39075             
39076             if(this.fireEvent('beforepush', this, v) !== false){
39077                 var d = (this.doc.body || this.doc.documentElement);
39078                 d.innerHTML = v;
39079                 this.cleanUpPaste();
39080                 this.el.dom.value = d.innerHTML;
39081                 this.fireEvent('push', this, v);
39082             }
39083         }
39084     },
39085
39086     // private
39087     deferFocus : function(){
39088         this.focus.defer(10, this);
39089     },
39090
39091     // doc'ed in Field
39092     focus : function(){
39093         if(this.win && !this.sourceEditMode){
39094             this.win.focus();
39095         }else{
39096             this.el.focus();
39097         }
39098     },
39099     
39100     assignDocWin: function()
39101     {
39102         var iframe = this.iframe;
39103         
39104          if(Roo.isIE){
39105             this.doc = iframe.contentWindow.document;
39106             this.win = iframe.contentWindow;
39107         } else {
39108             if (!Roo.get(this.frameId)) {
39109                 return;
39110             }
39111             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39112             this.win = Roo.get(this.frameId).dom.contentWindow;
39113         }
39114     },
39115     
39116     // private
39117     initEditor : function(){
39118         //console.log("INIT EDITOR");
39119         this.assignDocWin();
39120         
39121         
39122         
39123         this.doc.designMode="on";
39124         this.doc.open();
39125         this.doc.write(this.getDocMarkup());
39126         this.doc.close();
39127         
39128         var dbody = (this.doc.body || this.doc.documentElement);
39129         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39130         // this copies styles from the containing element into thsi one..
39131         // not sure why we need all of this..
39132         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39133         ss['background-attachment'] = 'fixed'; // w3c
39134         dbody.bgProperties = 'fixed'; // ie
39135         Roo.DomHelper.applyStyles(dbody, ss);
39136         Roo.EventManager.on(this.doc, {
39137             //'mousedown': this.onEditorEvent,
39138             'mouseup': this.onEditorEvent,
39139             'dblclick': this.onEditorEvent,
39140             'click': this.onEditorEvent,
39141             'keyup': this.onEditorEvent,
39142             buffer:100,
39143             scope: this
39144         });
39145         if(Roo.isGecko){
39146             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39147         }
39148         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39149             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39150         }
39151         this.initialized = true;
39152
39153         this.fireEvent('initialize', this);
39154         this.pushValue();
39155     },
39156
39157     // private
39158     onDestroy : function(){
39159         
39160         
39161         
39162         if(this.rendered){
39163             
39164             for (var i =0; i < this.toolbars.length;i++) {
39165                 // fixme - ask toolbars for heights?
39166                 this.toolbars[i].onDestroy();
39167             }
39168             
39169             this.wrap.dom.innerHTML = '';
39170             this.wrap.remove();
39171         }
39172     },
39173
39174     // private
39175     onFirstFocus : function(){
39176         
39177         this.assignDocWin();
39178         
39179         
39180         this.activated = true;
39181         for (var i =0; i < this.toolbars.length;i++) {
39182             this.toolbars[i].onFirstFocus();
39183         }
39184        
39185         if(Roo.isGecko){ // prevent silly gecko errors
39186             this.win.focus();
39187             var s = this.win.getSelection();
39188             if(!s.focusNode || s.focusNode.nodeType != 3){
39189                 var r = s.getRangeAt(0);
39190                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39191                 r.collapse(true);
39192                 this.deferFocus();
39193             }
39194             try{
39195                 this.execCmd('useCSS', true);
39196                 this.execCmd('styleWithCSS', false);
39197             }catch(e){}
39198         }
39199         this.fireEvent('activate', this);
39200     },
39201
39202     // private
39203     adjustFont: function(btn){
39204         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39205         //if(Roo.isSafari){ // safari
39206         //    adjust *= 2;
39207        // }
39208         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39209         if(Roo.isSafari){ // safari
39210             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39211             v =  (v < 10) ? 10 : v;
39212             v =  (v > 48) ? 48 : v;
39213             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39214             
39215         }
39216         
39217         
39218         v = Math.max(1, v+adjust);
39219         
39220         this.execCmd('FontSize', v  );
39221     },
39222
39223     onEditorEvent : function(e){
39224         this.fireEvent('editorevent', this, e);
39225       //  this.updateToolbar();
39226         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
39227     },
39228
39229     insertTag : function(tg)
39230     {
39231         // could be a bit smarter... -> wrap the current selected tRoo..
39232         
39233         this.execCmd("formatblock",   tg);
39234         
39235     },
39236     
39237     insertText : function(txt)
39238     {
39239         
39240         
39241         range = this.createRange();
39242         range.deleteContents();
39243                //alert(Sender.getAttribute('label'));
39244                
39245         range.insertNode(this.doc.createTextNode(txt));
39246     } ,
39247     
39248     // private
39249     relayBtnCmd : function(btn){
39250         this.relayCmd(btn.cmd);
39251     },
39252
39253     /**
39254      * Executes a Midas editor command on the editor document and performs necessary focus and
39255      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39256      * @param {String} cmd The Midas command
39257      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39258      */
39259     relayCmd : function(cmd, value){
39260         this.win.focus();
39261         this.execCmd(cmd, value);
39262         this.fireEvent('editorevent', this);
39263         //this.updateToolbar();
39264         this.deferFocus();
39265     },
39266
39267     /**
39268      * Executes a Midas editor command directly on the editor document.
39269      * For visual commands, you should use {@link #relayCmd} instead.
39270      * <b>This should only be called after the editor is initialized.</b>
39271      * @param {String} cmd The Midas command
39272      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39273      */
39274     execCmd : function(cmd, value){
39275         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39276         this.syncValue();
39277     },
39278  
39279  
39280    
39281     /**
39282      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39283      * to insert tRoo.
39284      * @param {String} text | dom node.. 
39285      */
39286     insertAtCursor : function(text)
39287     {
39288         
39289         
39290         
39291         if(!this.activated){
39292             return;
39293         }
39294         /*
39295         if(Roo.isIE){
39296             this.win.focus();
39297             var r = this.doc.selection.createRange();
39298             if(r){
39299                 r.collapse(true);
39300                 r.pasteHTML(text);
39301                 this.syncValue();
39302                 this.deferFocus();
39303             
39304             }
39305             return;
39306         }
39307         */
39308         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39309             this.win.focus();
39310             
39311             
39312             // from jquery ui (MIT licenced)
39313             var range, node;
39314             var win = this.win;
39315             
39316             if (win.getSelection && win.getSelection().getRangeAt) {
39317                 range = win.getSelection().getRangeAt(0);
39318                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
39319                 range.insertNode(node);
39320             } else if (win.document.selection && win.document.selection.createRange) {
39321                 // no firefox support
39322                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39323                 win.document.selection.createRange().pasteHTML(txt);
39324             } else {
39325                 // no firefox support
39326                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39327                 this.execCmd('InsertHTML', txt);
39328             } 
39329             
39330             this.syncValue();
39331             
39332             this.deferFocus();
39333         }
39334     },
39335  // private
39336     mozKeyPress : function(e){
39337         if(e.ctrlKey){
39338             var c = e.getCharCode(), cmd;
39339           
39340             if(c > 0){
39341                 c = String.fromCharCode(c).toLowerCase();
39342                 switch(c){
39343                     case 'b':
39344                         cmd = 'bold';
39345                         break;
39346                     case 'i':
39347                         cmd = 'italic';
39348                         break;
39349                     
39350                     case 'u':
39351                         cmd = 'underline';
39352                         break;
39353                     
39354                     case 'v':
39355                         this.cleanUpPaste.defer(100, this);
39356                         return;
39357                         
39358                 }
39359                 if(cmd){
39360                     this.win.focus();
39361                     this.execCmd(cmd);
39362                     this.deferFocus();
39363                     e.preventDefault();
39364                 }
39365                 
39366             }
39367         }
39368     },
39369
39370     // private
39371     fixKeys : function(){ // load time branching for fastest keydown performance
39372         if(Roo.isIE){
39373             return function(e){
39374                 var k = e.getKey(), r;
39375                 if(k == e.TAB){
39376                     e.stopEvent();
39377                     r = this.doc.selection.createRange();
39378                     if(r){
39379                         r.collapse(true);
39380                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39381                         this.deferFocus();
39382                     }
39383                     return;
39384                 }
39385                 
39386                 if(k == e.ENTER){
39387                     r = this.doc.selection.createRange();
39388                     if(r){
39389                         var target = r.parentElement();
39390                         if(!target || target.tagName.toLowerCase() != 'li'){
39391                             e.stopEvent();
39392                             r.pasteHTML('<br />');
39393                             r.collapse(false);
39394                             r.select();
39395                         }
39396                     }
39397                 }
39398                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39399                     this.cleanUpPaste.defer(100, this);
39400                     return;
39401                 }
39402                 
39403                 
39404             };
39405         }else if(Roo.isOpera){
39406             return function(e){
39407                 var k = e.getKey();
39408                 if(k == e.TAB){
39409                     e.stopEvent();
39410                     this.win.focus();
39411                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39412                     this.deferFocus();
39413                 }
39414                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39415                     this.cleanUpPaste.defer(100, this);
39416                     return;
39417                 }
39418                 
39419             };
39420         }else if(Roo.isSafari){
39421             return function(e){
39422                 var k = e.getKey();
39423                 
39424                 if(k == e.TAB){
39425                     e.stopEvent();
39426                     this.execCmd('InsertText','\t');
39427                     this.deferFocus();
39428                     return;
39429                 }
39430                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39431                     this.cleanUpPaste.defer(100, this);
39432                     return;
39433                 }
39434                 
39435              };
39436         }
39437     }(),
39438     
39439     getAllAncestors: function()
39440     {
39441         var p = this.getSelectedNode();
39442         var a = [];
39443         if (!p) {
39444             a.push(p); // push blank onto stack..
39445             p = this.getParentElement();
39446         }
39447         
39448         
39449         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39450             a.push(p);
39451             p = p.parentNode;
39452         }
39453         a.push(this.doc.body);
39454         return a;
39455     },
39456     lastSel : false,
39457     lastSelNode : false,
39458     
39459     
39460     getSelection : function() 
39461     {
39462         this.assignDocWin();
39463         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39464     },
39465     
39466     getSelectedNode: function() 
39467     {
39468         // this may only work on Gecko!!!
39469         
39470         // should we cache this!!!!
39471         
39472         
39473         
39474          
39475         var range = this.createRange(this.getSelection()).cloneRange();
39476         
39477         if (Roo.isIE) {
39478             var parent = range.parentElement();
39479             while (true) {
39480                 var testRange = range.duplicate();
39481                 testRange.moveToElementText(parent);
39482                 if (testRange.inRange(range)) {
39483                     break;
39484                 }
39485                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39486                     break;
39487                 }
39488                 parent = parent.parentElement;
39489             }
39490             return parent;
39491         }
39492         
39493         // is ancestor a text element.
39494         var ac =  range.commonAncestorContainer;
39495         if (ac.nodeType == 3) {
39496             ac = ac.parentNode;
39497         }
39498         
39499         var ar = ac.childNodes;
39500          
39501         var nodes = [];
39502         var other_nodes = [];
39503         var has_other_nodes = false;
39504         for (var i=0;i<ar.length;i++) {
39505             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39506                 continue;
39507             }
39508             // fullly contained node.
39509             
39510             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39511                 nodes.push(ar[i]);
39512                 continue;
39513             }
39514             
39515             // probably selected..
39516             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39517                 other_nodes.push(ar[i]);
39518                 continue;
39519             }
39520             // outer..
39521             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39522                 continue;
39523             }
39524             
39525             
39526             has_other_nodes = true;
39527         }
39528         if (!nodes.length && other_nodes.length) {
39529             nodes= other_nodes;
39530         }
39531         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39532             return false;
39533         }
39534         
39535         return nodes[0];
39536     },
39537     createRange: function(sel)
39538     {
39539         // this has strange effects when using with 
39540         // top toolbar - not sure if it's a great idea.
39541         //this.editor.contentWindow.focus();
39542         if (typeof sel != "undefined") {
39543             try {
39544                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39545             } catch(e) {
39546                 return this.doc.createRange();
39547             }
39548         } else {
39549             return this.doc.createRange();
39550         }
39551     },
39552     getParentElement: function()
39553     {
39554         
39555         this.assignDocWin();
39556         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39557         
39558         var range = this.createRange(sel);
39559          
39560         try {
39561             var p = range.commonAncestorContainer;
39562             while (p.nodeType == 3) { // text node
39563                 p = p.parentNode;
39564             }
39565             return p;
39566         } catch (e) {
39567             return null;
39568         }
39569     
39570     },
39571     /***
39572      *
39573      * Range intersection.. the hard stuff...
39574      *  '-1' = before
39575      *  '0' = hits..
39576      *  '1' = after.
39577      *         [ -- selected range --- ]
39578      *   [fail]                        [fail]
39579      *
39580      *    basically..
39581      *      if end is before start or  hits it. fail.
39582      *      if start is after end or hits it fail.
39583      *
39584      *   if either hits (but other is outside. - then it's not 
39585      *   
39586      *    
39587      **/
39588     
39589     
39590     // @see http://www.thismuchiknow.co.uk/?p=64.
39591     rangeIntersectsNode : function(range, node)
39592     {
39593         var nodeRange = node.ownerDocument.createRange();
39594         try {
39595             nodeRange.selectNode(node);
39596         } catch (e) {
39597             nodeRange.selectNodeContents(node);
39598         }
39599     
39600         var rangeStartRange = range.cloneRange();
39601         rangeStartRange.collapse(true);
39602     
39603         var rangeEndRange = range.cloneRange();
39604         rangeEndRange.collapse(false);
39605     
39606         var nodeStartRange = nodeRange.cloneRange();
39607         nodeStartRange.collapse(true);
39608     
39609         var nodeEndRange = nodeRange.cloneRange();
39610         nodeEndRange.collapse(false);
39611     
39612         return rangeStartRange.compareBoundaryPoints(
39613                  Range.START_TO_START, nodeEndRange) == -1 &&
39614                rangeEndRange.compareBoundaryPoints(
39615                  Range.START_TO_START, nodeStartRange) == 1;
39616         
39617          
39618     },
39619     rangeCompareNode : function(range, node)
39620     {
39621         var nodeRange = node.ownerDocument.createRange();
39622         try {
39623             nodeRange.selectNode(node);
39624         } catch (e) {
39625             nodeRange.selectNodeContents(node);
39626         }
39627         
39628         
39629         range.collapse(true);
39630     
39631         nodeRange.collapse(true);
39632      
39633         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39634         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39635          
39636         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39637         
39638         var nodeIsBefore   =  ss == 1;
39639         var nodeIsAfter    = ee == -1;
39640         
39641         if (nodeIsBefore && nodeIsAfter)
39642             return 0; // outer
39643         if (!nodeIsBefore && nodeIsAfter)
39644             return 1; //right trailed.
39645         
39646         if (nodeIsBefore && !nodeIsAfter)
39647             return 2;  // left trailed.
39648         // fully contined.
39649         return 3;
39650     },
39651
39652     // private? - in a new class?
39653     cleanUpPaste :  function()
39654     {
39655         // cleans up the whole document..
39656          Roo.log('cleanuppaste');
39657         this.cleanUpChildren(this.doc.body);
39658         var clean = this.cleanWordChars(this.doc.body.innerHTML);
39659         if (clean != this.doc.body.innerHTML) {
39660             this.doc.body.innerHTML = clean;
39661         }
39662         
39663     },
39664     
39665     cleanWordChars : function(input) {
39666         var he = Roo.form.HtmlEditor;
39667     
39668         var output = input;
39669         Roo.each(he.swapCodes, function(sw) { 
39670         
39671             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39672             output = output.replace(swapper, sw[1]);
39673         });
39674         return output;
39675     },
39676     
39677     
39678     cleanUpChildren : function (n)
39679     {
39680         if (!n.childNodes.length) {
39681             return;
39682         }
39683         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39684            this.cleanUpChild(n.childNodes[i]);
39685         }
39686     },
39687     
39688     
39689         
39690     
39691     cleanUpChild : function (node)
39692     {
39693         //console.log(node);
39694         if (node.nodeName == "#text") {
39695             // clean up silly Windows -- stuff?
39696             return; 
39697         }
39698         if (node.nodeName == "#comment") {
39699             node.parentNode.removeChild(node);
39700             // clean up silly Windows -- stuff?
39701             return; 
39702         }
39703         
39704         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39705             // remove node.
39706             node.parentNode.removeChild(node);
39707             return;
39708             
39709         }
39710         
39711         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39712         
39713         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39714         
39715         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39716             remove_keep_children = true;
39717         }
39718         
39719         if (remove_keep_children) {
39720             this.cleanUpChildren(node);
39721             // inserts everything just before this node...
39722             while (node.childNodes.length) {
39723                 var cn = node.childNodes[0];
39724                 node.removeChild(cn);
39725                 node.parentNode.insertBefore(cn, node);
39726             }
39727             node.parentNode.removeChild(node);
39728             return;
39729         }
39730         
39731         if (!node.attributes || !node.attributes.length) {
39732             this.cleanUpChildren(node);
39733             return;
39734         }
39735         
39736         function cleanAttr(n,v)
39737         {
39738             
39739             if (v.match(/^\./) || v.match(/^\//)) {
39740                 return;
39741             }
39742             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39743                 return;
39744             }
39745             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39746             node.removeAttribute(n);
39747             
39748         }
39749         
39750         function cleanStyle(n,v)
39751         {
39752             if (v.match(/expression/)) { //XSS?? should we even bother..
39753                 node.removeAttribute(n);
39754                 return;
39755             }
39756             
39757             
39758             var parts = v.split(/;/);
39759             Roo.each(parts, function(p) {
39760                 p = p.replace(/\s+/g,'');
39761                 if (!p.length) {
39762                     return true;
39763                 }
39764                 var l = p.split(':').shift().replace(/\s+/g,'');
39765                 
39766                 // only allow 'c whitelisted system attributes'
39767                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39768                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39769                     node.removeAttribute(n);
39770                     return false;
39771                 }
39772                 return true;
39773             });
39774             
39775             
39776         }
39777         
39778         
39779         for (var i = node.attributes.length-1; i > -1 ; i--) {
39780             var a = node.attributes[i];
39781             //console.log(a);
39782             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39783                 node.removeAttribute(a.name);
39784                 return;
39785             }
39786             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39787                 cleanAttr(a.name,a.value); // fixme..
39788                 return;
39789             }
39790             if (a.name == 'style') {
39791                 cleanStyle(a.name,a.value);
39792             }
39793             /// clean up MS crap..
39794             // tecnically this should be a list of valid class'es..
39795             
39796             
39797             if (a.name == 'class') {
39798                 if (a.value.match(/^Mso/)) {
39799                     node.className = '';
39800                 }
39801                 
39802                 if (a.value.match(/body/)) {
39803                     node.className = '';
39804                 }
39805             }
39806             
39807             // style cleanup!?
39808             // class cleanup?
39809             
39810         }
39811         
39812         
39813         this.cleanUpChildren(node);
39814         
39815         
39816     }
39817     
39818     
39819     // hide stuff that is not compatible
39820     /**
39821      * @event blur
39822      * @hide
39823      */
39824     /**
39825      * @event change
39826      * @hide
39827      */
39828     /**
39829      * @event focus
39830      * @hide
39831      */
39832     /**
39833      * @event specialkey
39834      * @hide
39835      */
39836     /**
39837      * @cfg {String} fieldClass @hide
39838      */
39839     /**
39840      * @cfg {String} focusClass @hide
39841      */
39842     /**
39843      * @cfg {String} autoCreate @hide
39844      */
39845     /**
39846      * @cfg {String} inputType @hide
39847      */
39848     /**
39849      * @cfg {String} invalidClass @hide
39850      */
39851     /**
39852      * @cfg {String} invalidText @hide
39853      */
39854     /**
39855      * @cfg {String} msgFx @hide
39856      */
39857     /**
39858      * @cfg {String} validateOnBlur @hide
39859      */
39860 });
39861
39862 Roo.form.HtmlEditor.white = [
39863         'area', 'br', 'img', 'input', 'hr', 'wbr',
39864         
39865        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39866        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39867        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39868        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39869        'table',   'ul',         'xmp', 
39870        
39871        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39872       'thead',   'tr', 
39873      
39874       'dir', 'menu', 'ol', 'ul', 'dl',
39875        
39876       'embed',  'object'
39877 ];
39878
39879
39880 Roo.form.HtmlEditor.black = [
39881     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39882         'applet', // 
39883         'base',   'basefont', 'bgsound', 'blink',  'body', 
39884         'frame',  'frameset', 'head',    'html',   'ilayer', 
39885         'iframe', 'layer',  'link',     'meta',    'object',   
39886         'script', 'style' ,'title',  'xml' // clean later..
39887 ];
39888 Roo.form.HtmlEditor.clean = [
39889     'script', 'style', 'title', 'xml'
39890 ];
39891 Roo.form.HtmlEditor.remove = [
39892     'font'
39893 ];
39894 // attributes..
39895
39896 Roo.form.HtmlEditor.ablack = [
39897     'on'
39898 ];
39899     
39900 Roo.form.HtmlEditor.aclean = [ 
39901     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39902 ];
39903
39904 // protocols..
39905 Roo.form.HtmlEditor.pwhite= [
39906         'http',  'https',  'mailto'
39907 ];
39908
39909 // white listed style attributes.
39910 Roo.form.HtmlEditor.cwhite= [
39911         'text-align',
39912         'font-size'
39913 ];
39914
39915
39916 Roo.form.HtmlEditor.swapCodes   =[ 
39917     [    8211, "--" ], 
39918     [    8212, "--" ], 
39919     [    8216,  "'" ],  
39920     [    8217, "'" ],  
39921     [    8220, '"' ],  
39922     [    8221, '"' ],  
39923     [    8226, "*" ],  
39924     [    8230, "..." ]
39925 ]; 
39926
39927     // <script type="text/javascript">
39928 /*
39929  * Based on
39930  * Ext JS Library 1.1.1
39931  * Copyright(c) 2006-2007, Ext JS, LLC.
39932  *  
39933  
39934  */
39935
39936 /**
39937  * @class Roo.form.HtmlEditorToolbar1
39938  * Basic Toolbar
39939  * 
39940  * Usage:
39941  *
39942  new Roo.form.HtmlEditor({
39943     ....
39944     toolbars : [
39945         new Roo.form.HtmlEditorToolbar1({
39946             disable : { fonts: 1 , format: 1, ..., ... , ...],
39947             btns : [ .... ]
39948         })
39949     }
39950      
39951  * 
39952  * @cfg {Object} disable List of elements to disable..
39953  * @cfg {Array} btns List of additional buttons.
39954  * 
39955  * 
39956  * NEEDS Extra CSS? 
39957  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39958  */
39959  
39960 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39961 {
39962     
39963     Roo.apply(this, config);
39964     
39965     // default disabled, based on 'good practice'..
39966     this.disable = this.disable || {};
39967     Roo.applyIf(this.disable, {
39968         fontSize : true,
39969         colors : true,
39970         specialElements : true
39971     });
39972     
39973     
39974     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39975     // dont call parent... till later.
39976 }
39977
39978 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39979     
39980     tb: false,
39981     
39982     rendered: false,
39983     
39984     editor : false,
39985     /**
39986      * @cfg {Object} disable  List of toolbar elements to disable
39987          
39988      */
39989     disable : false,
39990       /**
39991      * @cfg {Array} fontFamilies An array of available font families
39992      */
39993     fontFamilies : [
39994         'Arial',
39995         'Courier New',
39996         'Tahoma',
39997         'Times New Roman',
39998         'Verdana'
39999     ],
40000     
40001     specialChars : [
40002            "&#169;",
40003           "&#174;",     
40004           "&#8482;",    
40005           "&#163;" ,    
40006          // "&#8212;",    
40007           "&#8230;",    
40008           "&#247;" ,    
40009         //  "&#225;" ,     ?? a acute?
40010            "&#8364;"    , //Euro
40011        //   "&#8220;"    ,
40012         //  "&#8221;"    ,
40013         //  "&#8226;"    ,
40014           "&#176;"  //   , // degrees
40015
40016          // "&#233;"     , // e ecute
40017          // "&#250;"     , // u ecute?
40018     ],
40019     
40020     specialElements : [
40021         {
40022             text: "Insert Table",
40023             xtype: 'MenuItem',
40024             xns : Roo.Menu,
40025             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
40026                 
40027         },
40028         {    
40029             text: "Insert Image",
40030             xtype: 'MenuItem',
40031             xns : Roo.Menu,
40032             ihtml : '<img src="about:blank"/>'
40033             
40034         }
40035         
40036          
40037     ],
40038     
40039     
40040     inputElements : [ 
40041             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
40042             "input:submit", "input:button", "select", "textarea", "label" ],
40043     formats : [
40044         ["p"] ,  
40045         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
40046         ["pre"],[ "code"], 
40047         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
40048     ],
40049      /**
40050      * @cfg {String} defaultFont default font to use.
40051      */
40052     defaultFont: 'tahoma',
40053    
40054     fontSelect : false,
40055     
40056     
40057     formatCombo : false,
40058     
40059     init : function(editor)
40060     {
40061         this.editor = editor;
40062         
40063         
40064         var fid = editor.frameId;
40065         var etb = this;
40066         function btn(id, toggle, handler){
40067             var xid = fid + '-'+ id ;
40068             return {
40069                 id : xid,
40070                 cmd : id,
40071                 cls : 'x-btn-icon x-edit-'+id,
40072                 enableToggle:toggle !== false,
40073                 scope: editor, // was editor...
40074                 handler:handler||editor.relayBtnCmd,
40075                 clickEvent:'mousedown',
40076                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40077                 tabIndex:-1
40078             };
40079         }
40080         
40081         
40082         
40083         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40084         this.tb = tb;
40085          // stop form submits
40086         tb.el.on('click', function(e){
40087             e.preventDefault(); // what does this do?
40088         });
40089
40090         if(!this.disable.font && !Roo.isSafari){
40091             /* why no safari for fonts
40092             editor.fontSelect = tb.el.createChild({
40093                 tag:'select',
40094                 tabIndex: -1,
40095                 cls:'x-font-select',
40096                 html: editor.createFontOptions()
40097             });
40098             editor.fontSelect.on('change', function(){
40099                 var font = editor.fontSelect.dom.value;
40100                 editor.relayCmd('fontname', font);
40101                 editor.deferFocus();
40102             }, editor);
40103             tb.add(
40104                 editor.fontSelect.dom,
40105                 '-'
40106             );
40107             */
40108         };
40109         if(!this.disable.formats){
40110             this.formatCombo = new Roo.form.ComboBox({
40111                 store: new Roo.data.SimpleStore({
40112                     id : 'tag',
40113                     fields: ['tag'],
40114                     data : this.formats // from states.js
40115                 }),
40116                 blockFocus : true,
40117                 //autoCreate : {tag: "div",  size: "20"},
40118                 displayField:'tag',
40119                 typeAhead: false,
40120                 mode: 'local',
40121                 editable : false,
40122                 triggerAction: 'all',
40123                 emptyText:'Add tag',
40124                 selectOnFocus:true,
40125                 width:135,
40126                 listeners : {
40127                     'select': function(c, r, i) {
40128                         editor.insertTag(r.get('tag'));
40129                         editor.focus();
40130                     }
40131                 }
40132
40133             });
40134             tb.addField(this.formatCombo);
40135             
40136         }
40137         
40138         if(!this.disable.format){
40139             tb.add(
40140                 btn('bold'),
40141                 btn('italic'),
40142                 btn('underline')
40143             );
40144         };
40145         if(!this.disable.fontSize){
40146             tb.add(
40147                 '-',
40148                 
40149                 
40150                 btn('increasefontsize', false, editor.adjustFont),
40151                 btn('decreasefontsize', false, editor.adjustFont)
40152             );
40153         };
40154         
40155         
40156         if(!this.disable.colors){
40157             tb.add(
40158                 '-', {
40159                     id:editor.frameId +'-forecolor',
40160                     cls:'x-btn-icon x-edit-forecolor',
40161                     clickEvent:'mousedown',
40162                     tooltip: this.buttonTips['forecolor'] || undefined,
40163                     tabIndex:-1,
40164                     menu : new Roo.menu.ColorMenu({
40165                         allowReselect: true,
40166                         focus: Roo.emptyFn,
40167                         value:'000000',
40168                         plain:true,
40169                         selectHandler: function(cp, color){
40170                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40171                             editor.deferFocus();
40172                         },
40173                         scope: editor,
40174                         clickEvent:'mousedown'
40175                     })
40176                 }, {
40177                     id:editor.frameId +'backcolor',
40178                     cls:'x-btn-icon x-edit-backcolor',
40179                     clickEvent:'mousedown',
40180                     tooltip: this.buttonTips['backcolor'] || undefined,
40181                     tabIndex:-1,
40182                     menu : new Roo.menu.ColorMenu({
40183                         focus: Roo.emptyFn,
40184                         value:'FFFFFF',
40185                         plain:true,
40186                         allowReselect: true,
40187                         selectHandler: function(cp, color){
40188                             if(Roo.isGecko){
40189                                 editor.execCmd('useCSS', false);
40190                                 editor.execCmd('hilitecolor', color);
40191                                 editor.execCmd('useCSS', true);
40192                                 editor.deferFocus();
40193                             }else{
40194                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40195                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40196                                 editor.deferFocus();
40197                             }
40198                         },
40199                         scope:editor,
40200                         clickEvent:'mousedown'
40201                     })
40202                 }
40203             );
40204         };
40205         // now add all the items...
40206         
40207
40208         if(!this.disable.alignments){
40209             tb.add(
40210                 '-',
40211                 btn('justifyleft'),
40212                 btn('justifycenter'),
40213                 btn('justifyright')
40214             );
40215         };
40216
40217         //if(!Roo.isSafari){
40218             if(!this.disable.links){
40219                 tb.add(
40220                     '-',
40221                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40222                 );
40223             };
40224
40225             if(!this.disable.lists){
40226                 tb.add(
40227                     '-',
40228                     btn('insertorderedlist'),
40229                     btn('insertunorderedlist')
40230                 );
40231             }
40232             if(!this.disable.sourceEdit){
40233                 tb.add(
40234                     '-',
40235                     btn('sourceedit', true, function(btn){
40236                         this.toggleSourceEdit(btn.pressed);
40237                     })
40238                 );
40239             }
40240         //}
40241         
40242         var smenu = { };
40243         // special menu.. - needs to be tidied up..
40244         if (!this.disable.special) {
40245             smenu = {
40246                 text: "&#169;",
40247                 cls: 'x-edit-none',
40248                 
40249                 menu : {
40250                     items : []
40251                 }
40252             };
40253             for (var i =0; i < this.specialChars.length; i++) {
40254                 smenu.menu.items.push({
40255                     
40256                     html: this.specialChars[i],
40257                     handler: function(a,b) {
40258                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40259                         //editor.insertAtCursor(a.html);
40260                         
40261                     },
40262                     tabIndex:-1
40263                 });
40264             }
40265             
40266             
40267             tb.add(smenu);
40268             
40269             
40270         }
40271          
40272         if (!this.disable.specialElements) {
40273             var semenu = {
40274                 text: "Other;",
40275                 cls: 'x-edit-none',
40276                 menu : {
40277                     items : []
40278                 }
40279             };
40280             for (var i =0; i < this.specialElements.length; i++) {
40281                 semenu.menu.items.push(
40282                     Roo.apply({ 
40283                         handler: function(a,b) {
40284                             editor.insertAtCursor(this.ihtml);
40285                         }
40286                     }, this.specialElements[i])
40287                 );
40288                     
40289             }
40290             
40291             tb.add(semenu);
40292             
40293             
40294         }
40295          
40296         
40297         if (this.btns) {
40298             for(var i =0; i< this.btns.length;i++) {
40299                 var b = this.btns[i];
40300                 b.cls =  'x-edit-none';
40301                 b.scope = editor;
40302                 tb.add(b);
40303             }
40304         
40305         }
40306         
40307         
40308         
40309         // disable everything...
40310         
40311         this.tb.items.each(function(item){
40312            if(item.id != editor.frameId+ '-sourceedit'){
40313                 item.disable();
40314             }
40315         });
40316         this.rendered = true;
40317         
40318         // the all the btns;
40319         editor.on('editorevent', this.updateToolbar, this);
40320         // other toolbars need to implement this..
40321         //editor.on('editmodechange', this.updateToolbar, this);
40322     },
40323     
40324     
40325     
40326     /**
40327      * Protected method that will not generally be called directly. It triggers
40328      * a toolbar update by reading the markup state of the current selection in the editor.
40329      */
40330     updateToolbar: function(){
40331
40332         if(!this.editor.activated){
40333             this.editor.onFirstFocus();
40334             return;
40335         }
40336
40337         var btns = this.tb.items.map, 
40338             doc = this.editor.doc,
40339             frameId = this.editor.frameId;
40340
40341         if(!this.disable.font && !Roo.isSafari){
40342             /*
40343             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40344             if(name != this.fontSelect.dom.value){
40345                 this.fontSelect.dom.value = name;
40346             }
40347             */
40348         }
40349         if(!this.disable.format){
40350             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40351             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40352             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40353         }
40354         if(!this.disable.alignments){
40355             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40356             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40357             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40358         }
40359         if(!Roo.isSafari && !this.disable.lists){
40360             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40361             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40362         }
40363         
40364         var ans = this.editor.getAllAncestors();
40365         if (this.formatCombo) {
40366             
40367             
40368             var store = this.formatCombo.store;
40369             this.formatCombo.setValue("");
40370             for (var i =0; i < ans.length;i++) {
40371                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40372                     // select it..
40373                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40374                     break;
40375                 }
40376             }
40377         }
40378         
40379         
40380         
40381         // hides menus... - so this cant be on a menu...
40382         Roo.menu.MenuMgr.hideAll();
40383
40384         //this.editorsyncValue();
40385     },
40386    
40387     
40388     createFontOptions : function(){
40389         var buf = [], fs = this.fontFamilies, ff, lc;
40390         for(var i = 0, len = fs.length; i< len; i++){
40391             ff = fs[i];
40392             lc = ff.toLowerCase();
40393             buf.push(
40394                 '<option value="',lc,'" style="font-family:',ff,';"',
40395                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40396                     ff,
40397                 '</option>'
40398             );
40399         }
40400         return buf.join('');
40401     },
40402     
40403     toggleSourceEdit : function(sourceEditMode){
40404         if(sourceEditMode === undefined){
40405             sourceEditMode = !this.sourceEditMode;
40406         }
40407         this.sourceEditMode = sourceEditMode === true;
40408         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40409         // just toggle the button?
40410         if(btn.pressed !== this.editor.sourceEditMode){
40411             btn.toggle(this.editor.sourceEditMode);
40412             return;
40413         }
40414         
40415         if(this.sourceEditMode){
40416             this.tb.items.each(function(item){
40417                 if(item.cmd != 'sourceedit'){
40418                     item.disable();
40419                 }
40420             });
40421           
40422         }else{
40423             if(this.initialized){
40424                 this.tb.items.each(function(item){
40425                     item.enable();
40426                 });
40427             }
40428             
40429         }
40430         // tell the editor that it's been pressed..
40431         this.editor.toggleSourceEdit(sourceEditMode);
40432        
40433     },
40434      /**
40435      * Object collection of toolbar tooltips for the buttons in the editor. The key
40436      * is the command id associated with that button and the value is a valid QuickTips object.
40437      * For example:
40438 <pre><code>
40439 {
40440     bold : {
40441         title: 'Bold (Ctrl+B)',
40442         text: 'Make the selected text bold.',
40443         cls: 'x-html-editor-tip'
40444     },
40445     italic : {
40446         title: 'Italic (Ctrl+I)',
40447         text: 'Make the selected text italic.',
40448         cls: 'x-html-editor-tip'
40449     },
40450     ...
40451 </code></pre>
40452     * @type Object
40453      */
40454     buttonTips : {
40455         bold : {
40456             title: 'Bold (Ctrl+B)',
40457             text: 'Make the selected text bold.',
40458             cls: 'x-html-editor-tip'
40459         },
40460         italic : {
40461             title: 'Italic (Ctrl+I)',
40462             text: 'Make the selected text italic.',
40463             cls: 'x-html-editor-tip'
40464         },
40465         underline : {
40466             title: 'Underline (Ctrl+U)',
40467             text: 'Underline the selected text.',
40468             cls: 'x-html-editor-tip'
40469         },
40470         increasefontsize : {
40471             title: 'Grow Text',
40472             text: 'Increase the font size.',
40473             cls: 'x-html-editor-tip'
40474         },
40475         decreasefontsize : {
40476             title: 'Shrink Text',
40477             text: 'Decrease the font size.',
40478             cls: 'x-html-editor-tip'
40479         },
40480         backcolor : {
40481             title: 'Text Highlight Color',
40482             text: 'Change the background color of the selected text.',
40483             cls: 'x-html-editor-tip'
40484         },
40485         forecolor : {
40486             title: 'Font Color',
40487             text: 'Change the color of the selected text.',
40488             cls: 'x-html-editor-tip'
40489         },
40490         justifyleft : {
40491             title: 'Align Text Left',
40492             text: 'Align text to the left.',
40493             cls: 'x-html-editor-tip'
40494         },
40495         justifycenter : {
40496             title: 'Center Text',
40497             text: 'Center text in the editor.',
40498             cls: 'x-html-editor-tip'
40499         },
40500         justifyright : {
40501             title: 'Align Text Right',
40502             text: 'Align text to the right.',
40503             cls: 'x-html-editor-tip'
40504         },
40505         insertunorderedlist : {
40506             title: 'Bullet List',
40507             text: 'Start a bulleted list.',
40508             cls: 'x-html-editor-tip'
40509         },
40510         insertorderedlist : {
40511             title: 'Numbered List',
40512             text: 'Start a numbered list.',
40513             cls: 'x-html-editor-tip'
40514         },
40515         createlink : {
40516             title: 'Hyperlink',
40517             text: 'Make the selected text a hyperlink.',
40518             cls: 'x-html-editor-tip'
40519         },
40520         sourceedit : {
40521             title: 'Source Edit',
40522             text: 'Switch to source editing mode.',
40523             cls: 'x-html-editor-tip'
40524         }
40525     },
40526     // private
40527     onDestroy : function(){
40528         if(this.rendered){
40529             
40530             this.tb.items.each(function(item){
40531                 if(item.menu){
40532                     item.menu.removeAll();
40533                     if(item.menu.el){
40534                         item.menu.el.destroy();
40535                     }
40536                 }
40537                 item.destroy();
40538             });
40539              
40540         }
40541     },
40542     onFirstFocus: function() {
40543         this.tb.items.each(function(item){
40544            item.enable();
40545         });
40546     }
40547 });
40548
40549
40550
40551
40552 // <script type="text/javascript">
40553 /*
40554  * Based on
40555  * Ext JS Library 1.1.1
40556  * Copyright(c) 2006-2007, Ext JS, LLC.
40557  *  
40558  
40559  */
40560
40561  
40562 /**
40563  * @class Roo.form.HtmlEditor.ToolbarContext
40564  * Context Toolbar
40565  * 
40566  * Usage:
40567  *
40568  new Roo.form.HtmlEditor({
40569     ....
40570     toolbars : [
40571         { xtype: 'ToolbarStandard', styles : {} }
40572         { xtype: 'ToolbarContext', disable : {} }
40573     ]
40574 })
40575
40576      
40577  * 
40578  * @config : {Object} disable List of elements to disable.. (not done yet.)
40579  * @config : {Object} styles  Map of styles available.
40580  * 
40581  */
40582
40583 Roo.form.HtmlEditor.ToolbarContext = function(config)
40584 {
40585     
40586     Roo.apply(this, config);
40587     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40588     // dont call parent... till later.
40589     this.styles = this.styles || {};
40590 }
40591 Roo.form.HtmlEditor.ToolbarContext.types = {
40592     'IMG' : {
40593         width : {
40594             title: "Width",
40595             width: 40
40596         },
40597         height:  {
40598             title: "Height",
40599             width: 40
40600         },
40601         align: {
40602             title: "Align",
40603             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40604             width : 80
40605             
40606         },
40607         border: {
40608             title: "Border",
40609             width: 40
40610         },
40611         alt: {
40612             title: "Alt",
40613             width: 120
40614         },
40615         src : {
40616             title: "Src",
40617             width: 220
40618         }
40619         
40620     },
40621     'A' : {
40622         name : {
40623             title: "Name",
40624             width: 50
40625         },
40626         href:  {
40627             title: "Href",
40628             width: 220
40629         } // border?
40630         
40631     },
40632     'TABLE' : {
40633         rows : {
40634             title: "Rows",
40635             width: 20
40636         },
40637         cols : {
40638             title: "Cols",
40639             width: 20
40640         },
40641         width : {
40642             title: "Width",
40643             width: 40
40644         },
40645         height : {
40646             title: "Height",
40647             width: 40
40648         },
40649         border : {
40650             title: "Border",
40651             width: 20
40652         }
40653     },
40654     'TD' : {
40655         width : {
40656             title: "Width",
40657             width: 40
40658         },
40659         height : {
40660             title: "Height",
40661             width: 40
40662         },   
40663         align: {
40664             title: "Align",
40665             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40666             width: 80
40667         },
40668         valign: {
40669             title: "Valign",
40670             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40671             width: 80
40672         },
40673         colspan: {
40674             title: "Colspan",
40675             width: 20
40676             
40677         }
40678     },
40679     'INPUT' : {
40680         name : {
40681             title: "name",
40682             width: 120
40683         },
40684         value : {
40685             title: "Value",
40686             width: 120
40687         },
40688         width : {
40689             title: "Width",
40690             width: 40
40691         }
40692     },
40693     'LABEL' : {
40694         'for' : {
40695             title: "For",
40696             width: 120
40697         }
40698     },
40699     'TEXTAREA' : {
40700           name : {
40701             title: "name",
40702             width: 120
40703         },
40704         rows : {
40705             title: "Rows",
40706             width: 20
40707         },
40708         cols : {
40709             title: "Cols",
40710             width: 20
40711         }
40712     },
40713     'SELECT' : {
40714         name : {
40715             title: "name",
40716             width: 120
40717         },
40718         selectoptions : {
40719             title: "Options",
40720             width: 200
40721         }
40722     },
40723     
40724     // should we really allow this??
40725     // should this just be 
40726     'BODY' : {
40727         title : {
40728             title: "title",
40729             width: 200,
40730             disabled : true
40731         }
40732     },
40733     '*' : {
40734         // empty..
40735     }
40736 };
40737
40738
40739
40740 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40741     
40742     tb: false,
40743     
40744     rendered: false,
40745     
40746     editor : false,
40747     /**
40748      * @cfg {Object} disable  List of toolbar elements to disable
40749          
40750      */
40751     disable : false,
40752     /**
40753      * @cfg {Object} styles List of styles 
40754      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40755      *
40756      * These must be defined in the page, so they get rendered correctly..
40757      * .headline { }
40758      * TD.underline { }
40759      * 
40760      */
40761     styles : false,
40762     
40763     
40764     
40765     toolbars : false,
40766     
40767     init : function(editor)
40768     {
40769         this.editor = editor;
40770         
40771         
40772         var fid = editor.frameId;
40773         var etb = this;
40774         function btn(id, toggle, handler){
40775             var xid = fid + '-'+ id ;
40776             return {
40777                 id : xid,
40778                 cmd : id,
40779                 cls : 'x-btn-icon x-edit-'+id,
40780                 enableToggle:toggle !== false,
40781                 scope: editor, // was editor...
40782                 handler:handler||editor.relayBtnCmd,
40783                 clickEvent:'mousedown',
40784                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40785                 tabIndex:-1
40786             };
40787         }
40788         // create a new element.
40789         var wdiv = editor.wrap.createChild({
40790                 tag: 'div'
40791             }, editor.wrap.dom.firstChild.nextSibling, true);
40792         
40793         // can we do this more than once??
40794         
40795          // stop form submits
40796       
40797  
40798         // disable everything...
40799         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40800         this.toolbars = {};
40801            
40802         for (var i in  ty) {
40803           
40804             this.toolbars[i] = this.buildToolbar(ty[i],i);
40805         }
40806         this.tb = this.toolbars.BODY;
40807         this.tb.el.show();
40808         this.buildFooter();
40809         this.footer.show();
40810          
40811         this.rendered = true;
40812         
40813         // the all the btns;
40814         editor.on('editorevent', this.updateToolbar, this);
40815         // other toolbars need to implement this..
40816         //editor.on('editmodechange', this.updateToolbar, this);
40817     },
40818     
40819     
40820     
40821     /**
40822      * Protected method that will not generally be called directly. It triggers
40823      * a toolbar update by reading the markup state of the current selection in the editor.
40824      */
40825     updateToolbar: function(editor,ev,sel){
40826
40827         //Roo.log(ev);
40828         // capture mouse up - this is handy for selecting images..
40829         // perhaps should go somewhere else...
40830         if(!this.editor.activated){
40831              this.editor.onFirstFocus();
40832             return;
40833         }
40834         
40835         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
40836         // selectNode - might want to handle IE?
40837         if (ev &&
40838             (ev.type == 'mouseup' || ev.type == 'click' ) &&
40839             ev.target && ev.target.tagName == 'IMG') {
40840             // they have click on an image...
40841             // let's see if we can change the selection...
40842             sel = ev.target;
40843          
40844               var nodeRange = sel.ownerDocument.createRange();
40845             try {
40846                 nodeRange.selectNode(sel);
40847             } catch (e) {
40848                 nodeRange.selectNodeContents(sel);
40849             }
40850             //nodeRange.collapse(true);
40851             var s = editor.win.getSelection();
40852             s.removeAllRanges();
40853             s.addRange(nodeRange);
40854         }  
40855         
40856       
40857         var updateFooter = sel ? false : true;
40858         
40859         
40860         var ans = this.editor.getAllAncestors();
40861         
40862         // pick
40863         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40864         
40865         if (!sel) { 
40866             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40867             sel = sel ? sel : this.editor.doc.body;
40868             sel = sel.tagName.length ? sel : this.editor.doc.body;
40869             
40870         }
40871         // pick a menu that exists..
40872         var tn = sel.tagName.toUpperCase();
40873         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40874         
40875         tn = sel.tagName.toUpperCase();
40876         
40877         var lastSel = this.tb.selectedNode
40878         
40879         this.tb.selectedNode = sel;
40880         
40881         // if current menu does not match..
40882         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40883                 
40884             this.tb.el.hide();
40885             ///console.log("show: " + tn);
40886             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40887             this.tb.el.show();
40888             // update name
40889             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40890             
40891             
40892             // update attributes
40893             if (this.tb.fields) {
40894                 this.tb.fields.each(function(e) {
40895                    e.setValue(sel.getAttribute(e.name));
40896                 });
40897             }
40898             
40899             // update styles
40900             var st = this.tb.fields.item(0);
40901             st.store.removeAll();
40902             var cn = sel.className.split(/\s+/);
40903             
40904             var avs = [];
40905             if (this.styles['*']) {
40906                 
40907                 Roo.each(this.styles['*'], function(v) {
40908                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40909                 });
40910             }
40911             if (this.styles[tn]) { 
40912                 Roo.each(this.styles[tn], function(v) {
40913                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40914                 });
40915             }
40916             
40917             st.store.loadData(avs);
40918             st.collapse();
40919             st.setValue(cn);
40920             
40921             // flag our selected Node.
40922             this.tb.selectedNode = sel;
40923            
40924            
40925             Roo.menu.MenuMgr.hideAll();
40926
40927         }
40928         
40929         if (!updateFooter) {
40930             return;
40931         }
40932         // update the footer
40933         //
40934         var html = '';
40935         
40936         this.footerEls = ans.reverse();
40937         Roo.each(this.footerEls, function(a,i) {
40938             if (!a) { return; }
40939             html += html.length ? ' &gt; '  :  '';
40940             
40941             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40942             
40943         });
40944        
40945         // 
40946         var sz = this.footDisp.up('td').getSize();
40947         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40948         this.footDisp.dom.style.marginLeft = '5px';
40949         
40950         this.footDisp.dom.style.overflow = 'hidden';
40951         
40952         this.footDisp.dom.innerHTML = html;
40953             
40954         //this.editorsyncValue();
40955     },
40956    
40957        
40958     // private
40959     onDestroy : function(){
40960         if(this.rendered){
40961             
40962             this.tb.items.each(function(item){
40963                 if(item.menu){
40964                     item.menu.removeAll();
40965                     if(item.menu.el){
40966                         item.menu.el.destroy();
40967                     }
40968                 }
40969                 item.destroy();
40970             });
40971              
40972         }
40973     },
40974     onFirstFocus: function() {
40975         // need to do this for all the toolbars..
40976         this.tb.items.each(function(item){
40977            item.enable();
40978         });
40979     },
40980     buildToolbar: function(tlist, nm)
40981     {
40982         var editor = this.editor;
40983          // create a new element.
40984         var wdiv = editor.wrap.createChild({
40985                 tag: 'div'
40986             }, editor.wrap.dom.firstChild.nextSibling, true);
40987         
40988        
40989         var tb = new Roo.Toolbar(wdiv);
40990         // add the name..
40991         
40992         tb.add(nm+ ":&nbsp;");
40993         
40994         var styles = [];
40995         for(var i in this.styles) {
40996             styles.push(i);
40997         }
40998         
40999         // styles...
41000         if (styles && styles.length) {
41001             
41002             // this needs a multi-select checkbox...
41003             tb.addField( new Roo.form.ComboBox({
41004                 store: new Roo.data.SimpleStore({
41005                     id : 'val',
41006                     fields: ['val', 'selected'],
41007                     data : [] 
41008                 }),
41009                 name : 'className',
41010                 displayField:'val',
41011                 typeAhead: false,
41012                 mode: 'local',
41013                 editable : false,
41014                 triggerAction: 'all',
41015                 emptyText:'Select Style',
41016                 selectOnFocus:true,
41017                 width: 130,
41018                 listeners : {
41019                     'select': function(c, r, i) {
41020                         // initial support only for on class per el..
41021                         tb.selectedNode.className =  r ? r.get('val') : '';
41022                         editor.syncValue();
41023                     }
41024                 }
41025     
41026             }));
41027         }
41028             
41029         
41030         
41031         for (var i in tlist) {
41032             
41033             var item = tlist[i];
41034             tb.add(item.title + ":&nbsp;");
41035             
41036             
41037             
41038             
41039             if (item.opts) {
41040                 // opts == pulldown..
41041                 tb.addField( new Roo.form.ComboBox({
41042                     store: new Roo.data.SimpleStore({
41043                         id : 'val',
41044                         fields: ['val'],
41045                         data : item.opts  
41046                     }),
41047                     name : i,
41048                     displayField:'val',
41049                     typeAhead: false,
41050                     mode: 'local',
41051                     editable : false,
41052                     triggerAction: 'all',
41053                     emptyText:'Select',
41054                     selectOnFocus:true,
41055                     width: item.width ? item.width  : 130,
41056                     listeners : {
41057                         'select': function(c, r, i) {
41058                             tb.selectedNode.setAttribute(c.name, r.get('val'));
41059                         }
41060                     }
41061
41062                 }));
41063                 continue;
41064                     
41065                  
41066                 
41067                 tb.addField( new Roo.form.TextField({
41068                     name: i,
41069                     width: 100,
41070                     //allowBlank:false,
41071                     value: ''
41072                 }));
41073                 continue;
41074             }
41075             tb.addField( new Roo.form.TextField({
41076                 name: i,
41077                 width: item.width,
41078                 //allowBlank:true,
41079                 value: '',
41080                 listeners: {
41081                     'change' : function(f, nv, ov) {
41082                         tb.selectedNode.setAttribute(f.name, nv);
41083                     }
41084                 }
41085             }));
41086              
41087         }
41088         tb.el.on('click', function(e){
41089             e.preventDefault(); // what does this do?
41090         });
41091         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
41092         tb.el.hide();
41093         tb.name = nm;
41094         // dont need to disable them... as they will get hidden
41095         return tb;
41096          
41097         
41098     },
41099     buildFooter : function()
41100     {
41101         
41102         var fel = this.editor.wrap.createChild();
41103         this.footer = new Roo.Toolbar(fel);
41104         // toolbar has scrolly on left / right?
41105         var footDisp= new Roo.Toolbar.Fill();
41106         var _t = this;
41107         this.footer.add(
41108             {
41109                 text : '&lt;',
41110                 xtype: 'Button',
41111                 handler : function() {
41112                     _t.footDisp.scrollTo('left',0,true)
41113                 }
41114             }
41115         );
41116         this.footer.add( footDisp );
41117         this.footer.add( 
41118             {
41119                 text : '&gt;',
41120                 xtype: 'Button',
41121                 handler : function() {
41122                     // no animation..
41123                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41124                 }
41125             }
41126         );
41127         var fel = Roo.get(footDisp.el);
41128         fel.addClass('x-editor-context');
41129         this.footDispWrap = fel; 
41130         this.footDispWrap.overflow  = 'hidden';
41131         
41132         this.footDisp = fel.createChild();
41133         this.footDispWrap.on('click', this.onContextClick, this)
41134         
41135         
41136     },
41137     onContextClick : function (ev,dom)
41138     {
41139         ev.preventDefault();
41140         var  cn = dom.className;
41141         Roo.log(cn);
41142         if (!cn.match(/x-ed-loc-/)) {
41143             return;
41144         }
41145         var n = cn.split('-').pop();
41146         var ans = this.footerEls;
41147         var sel = ans[n];
41148         
41149          // pick
41150         var range = this.editor.createRange();
41151         
41152         range.selectNodeContents(sel);
41153         //range.selectNode(sel);
41154         
41155         
41156         var selection = this.editor.getSelection();
41157         selection.removeAllRanges();
41158         selection.addRange(range);
41159         
41160         
41161         
41162         this.updateToolbar(null, null, sel);
41163         
41164         
41165     }
41166     
41167     
41168     
41169     
41170     
41171 });
41172
41173
41174
41175
41176
41177 /*
41178  * Based on:
41179  * Ext JS Library 1.1.1
41180  * Copyright(c) 2006-2007, Ext JS, LLC.
41181  *
41182  * Originally Released Under LGPL - original licence link has changed is not relivant.
41183  *
41184  * Fork - LGPL
41185  * <script type="text/javascript">
41186  */
41187  
41188 /**
41189  * @class Roo.form.BasicForm
41190  * @extends Roo.util.Observable
41191  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41192  * @constructor
41193  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41194  * @param {Object} config Configuration options
41195  */
41196 Roo.form.BasicForm = function(el, config){
41197     this.allItems = [];
41198     this.childForms = [];
41199     Roo.apply(this, config);
41200     /*
41201      * The Roo.form.Field items in this form.
41202      * @type MixedCollection
41203      */
41204      
41205      
41206     this.items = new Roo.util.MixedCollection(false, function(o){
41207         return o.id || (o.id = Roo.id());
41208     });
41209     this.addEvents({
41210         /**
41211          * @event beforeaction
41212          * Fires before any action is performed. Return false to cancel the action.
41213          * @param {Form} this
41214          * @param {Action} action The action to be performed
41215          */
41216         beforeaction: true,
41217         /**
41218          * @event actionfailed
41219          * Fires when an action fails.
41220          * @param {Form} this
41221          * @param {Action} action The action that failed
41222          */
41223         actionfailed : true,
41224         /**
41225          * @event actioncomplete
41226          * Fires when an action is completed.
41227          * @param {Form} this
41228          * @param {Action} action The action that completed
41229          */
41230         actioncomplete : true
41231     });
41232     if(el){
41233         this.initEl(el);
41234     }
41235     Roo.form.BasicForm.superclass.constructor.call(this);
41236 };
41237
41238 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41239     /**
41240      * @cfg {String} method
41241      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41242      */
41243     /**
41244      * @cfg {DataReader} reader
41245      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41246      * This is optional as there is built-in support for processing JSON.
41247      */
41248     /**
41249      * @cfg {DataReader} errorReader
41250      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41251      * This is completely optional as there is built-in support for processing JSON.
41252      */
41253     /**
41254      * @cfg {String} url
41255      * The URL to use for form actions if one isn't supplied in the action options.
41256      */
41257     /**
41258      * @cfg {Boolean} fileUpload
41259      * Set to true if this form is a file upload.
41260      */
41261      
41262     /**
41263      * @cfg {Object} baseParams
41264      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41265      */
41266      /**
41267      
41268     /**
41269      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41270      */
41271     timeout: 30,
41272
41273     // private
41274     activeAction : null,
41275
41276     /**
41277      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41278      * or setValues() data instead of when the form was first created.
41279      */
41280     trackResetOnLoad : false,
41281     
41282     
41283     /**
41284      * childForms - used for multi-tab forms
41285      * @type {Array}
41286      */
41287     childForms : false,
41288     
41289     /**
41290      * allItems - full list of fields.
41291      * @type {Array}
41292      */
41293     allItems : false,
41294     
41295     /**
41296      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41297      * element by passing it or its id or mask the form itself by passing in true.
41298      * @type Mixed
41299      */
41300     waitMsgTarget : false,
41301
41302     // private
41303     initEl : function(el){
41304         this.el = Roo.get(el);
41305         this.id = this.el.id || Roo.id();
41306         this.el.on('submit', this.onSubmit, this);
41307         this.el.addClass('x-form');
41308     },
41309
41310     // private
41311     onSubmit : function(e){
41312         e.stopEvent();
41313     },
41314
41315     /**
41316      * Returns true if client-side validation on the form is successful.
41317      * @return Boolean
41318      */
41319     isValid : function(){
41320         var valid = true;
41321         this.items.each(function(f){
41322            if(!f.validate()){
41323                valid = false;
41324            }
41325         });
41326         return valid;
41327     },
41328
41329     /**
41330      * Returns true if any fields in this form have changed since their original load.
41331      * @return Boolean
41332      */
41333     isDirty : function(){
41334         var dirty = false;
41335         this.items.each(function(f){
41336            if(f.isDirty()){
41337                dirty = true;
41338                return false;
41339            }
41340         });
41341         return dirty;
41342     },
41343
41344     /**
41345      * Performs a predefined action (submit or load) or custom actions you define on this form.
41346      * @param {String} actionName The name of the action type
41347      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41348      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41349      * accept other config options):
41350      * <pre>
41351 Property          Type             Description
41352 ----------------  ---------------  ----------------------------------------------------------------------------------
41353 url               String           The url for the action (defaults to the form's url)
41354 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41355 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41356 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41357                                    validate the form on the client (defaults to false)
41358      * </pre>
41359      * @return {BasicForm} this
41360      */
41361     doAction : function(action, options){
41362         if(typeof action == 'string'){
41363             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41364         }
41365         if(this.fireEvent('beforeaction', this, action) !== false){
41366             this.beforeAction(action);
41367             action.run.defer(100, action);
41368         }
41369         return this;
41370     },
41371
41372     /**
41373      * Shortcut to do a submit action.
41374      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41375      * @return {BasicForm} this
41376      */
41377     submit : function(options){
41378         this.doAction('submit', options);
41379         return this;
41380     },
41381
41382     /**
41383      * Shortcut to do a load action.
41384      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41385      * @return {BasicForm} this
41386      */
41387     load : function(options){
41388         this.doAction('load', options);
41389         return this;
41390     },
41391
41392     /**
41393      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41394      * @param {Record} record The record to edit
41395      * @return {BasicForm} this
41396      */
41397     updateRecord : function(record){
41398         record.beginEdit();
41399         var fs = record.fields;
41400         fs.each(function(f){
41401             var field = this.findField(f.name);
41402             if(field){
41403                 record.set(f.name, field.getValue());
41404             }
41405         }, this);
41406         record.endEdit();
41407         return this;
41408     },
41409
41410     /**
41411      * Loads an Roo.data.Record into this form.
41412      * @param {Record} record The record to load
41413      * @return {BasicForm} this
41414      */
41415     loadRecord : function(record){
41416         this.setValues(record.data);
41417         return this;
41418     },
41419
41420     // private
41421     beforeAction : function(action){
41422         var o = action.options;
41423         
41424        
41425         if(this.waitMsgTarget === true){
41426             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41427         }else if(this.waitMsgTarget){
41428             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41429             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41430         }else {
41431             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41432         }
41433          
41434     },
41435
41436     // private
41437     afterAction : function(action, success){
41438         this.activeAction = null;
41439         var o = action.options;
41440         
41441         if(this.waitMsgTarget === true){
41442             this.el.unmask();
41443         }else if(this.waitMsgTarget){
41444             this.waitMsgTarget.unmask();
41445         }else{
41446             Roo.MessageBox.updateProgress(1);
41447             Roo.MessageBox.hide();
41448         }
41449          
41450         if(success){
41451             if(o.reset){
41452                 this.reset();
41453             }
41454             Roo.callback(o.success, o.scope, [this, action]);
41455             this.fireEvent('actioncomplete', this, action);
41456             
41457         }else{
41458             
41459             // failure condition..
41460             // we have a scenario where updates need confirming.
41461             // eg. if a locking scenario exists..
41462             // we look for { errors : { needs_confirm : true }} in the response.
41463             if (typeof(action.result.errors.needs_confirm) != 'undefined') {
41464                 var _t = this;
41465                 Roo.MessageBox.confirm(
41466                     "Change requires confirmation",
41467                     action.result.errorMsg,
41468                     function(r) {
41469                         if (r != 'yes') {
41470                             return;
41471                         }
41472                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
41473                     }
41474                     
41475                 );
41476                 
41477                 
41478                 
41479                 return;
41480             }
41481             
41482             Roo.callback(o.failure, o.scope, [this, action]);
41483             // show an error message if no failed handler is set..
41484             if (!this.hasListener('actionfailed')) {
41485                 Roo.MessageBox.alert("Error",
41486                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41487                         action.result.errorMsg :
41488                         "Saving Failed, please check your entries"
41489                 );
41490             }
41491             
41492             this.fireEvent('actionfailed', this, action);
41493         }
41494         
41495     },
41496
41497     /**
41498      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41499      * @param {String} id The value to search for
41500      * @return Field
41501      */
41502     findField : function(id){
41503         var field = this.items.get(id);
41504         if(!field){
41505             this.items.each(function(f){
41506                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41507                     field = f;
41508                     return false;
41509                 }
41510             });
41511         }
41512         return field || null;
41513     },
41514
41515     /**
41516      * Add a secondary form to this one, 
41517      * Used to provide tabbed forms. One form is primary, with hidden values 
41518      * which mirror the elements from the other forms.
41519      * 
41520      * @param {Roo.form.Form} form to add.
41521      * 
41522      */
41523     addForm : function(form)
41524     {
41525        
41526         if (this.childForms.indexOf(form) > -1) {
41527             // already added..
41528             return;
41529         }
41530         this.childForms.push(form);
41531         var n = '';
41532         Roo.each(form.allItems, function (fe) {
41533             
41534             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41535             if (this.findField(n)) { // already added..
41536                 return;
41537             }
41538             var add = new Roo.form.Hidden({
41539                 name : n
41540             });
41541             add.render(this.el);
41542             
41543             this.add( add );
41544         }, this);
41545         
41546     },
41547     /**
41548      * Mark fields in this form invalid in bulk.
41549      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41550      * @return {BasicForm} this
41551      */
41552     markInvalid : function(errors){
41553         if(errors instanceof Array){
41554             for(var i = 0, len = errors.length; i < len; i++){
41555                 var fieldError = errors[i];
41556                 var f = this.findField(fieldError.id);
41557                 if(f){
41558                     f.markInvalid(fieldError.msg);
41559                 }
41560             }
41561         }else{
41562             var field, id;
41563             for(id in errors){
41564                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41565                     field.markInvalid(errors[id]);
41566                 }
41567             }
41568         }
41569         Roo.each(this.childForms || [], function (f) {
41570             f.markInvalid(errors);
41571         });
41572         
41573         return this;
41574     },
41575
41576     /**
41577      * Set values for fields in this form in bulk.
41578      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41579      * @return {BasicForm} this
41580      */
41581     setValues : function(values){
41582         if(values instanceof Array){ // array of objects
41583             for(var i = 0, len = values.length; i < len; i++){
41584                 var v = values[i];
41585                 var f = this.findField(v.id);
41586                 if(f){
41587                     f.setValue(v.value);
41588                     if(this.trackResetOnLoad){
41589                         f.originalValue = f.getValue();
41590                     }
41591                 }
41592             }
41593         }else{ // object hash
41594             var field, id;
41595             for(id in values){
41596                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41597                     
41598                     if (field.setFromData && 
41599                         field.valueField && 
41600                         field.displayField &&
41601                         // combos' with local stores can 
41602                         // be queried via setValue()
41603                         // to set their value..
41604                         (field.store && !field.store.isLocal)
41605                         ) {
41606                         // it's a combo
41607                         var sd = { };
41608                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41609                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41610                         field.setFromData(sd);
41611                         
41612                     } else {
41613                         field.setValue(values[id]);
41614                     }
41615                     
41616                     
41617                     if(this.trackResetOnLoad){
41618                         field.originalValue = field.getValue();
41619                     }
41620                 }
41621             }
41622         }
41623          
41624         Roo.each(this.childForms || [], function (f) {
41625             f.setValues(values);
41626         });
41627                 
41628         return this;
41629     },
41630
41631     /**
41632      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41633      * they are returned as an array.
41634      * @param {Boolean} asString
41635      * @return {Object}
41636      */
41637     getValues : function(asString){
41638         if (this.childForms) {
41639             // copy values from the child forms
41640             Roo.each(this.childForms, function (f) {
41641                 this.setValues(f.getValues());
41642             }, this);
41643         }
41644         
41645         
41646         
41647         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41648         if(asString === true){
41649             return fs;
41650         }
41651         return Roo.urlDecode(fs);
41652     },
41653     
41654     /**
41655      * Returns the fields in this form as an object with key/value pairs. 
41656      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41657      * @return {Object}
41658      */
41659     getFieldValues : function(with_hidden)
41660     {
41661         if (this.childForms) {
41662             // copy values from the child forms
41663             // should this call getFieldValues - probably not as we do not currently copy
41664             // hidden fields when we generate..
41665             Roo.each(this.childForms, function (f) {
41666                 this.setValues(f.getValues());
41667             }, this);
41668         }
41669         
41670         var ret = {};
41671         this.items.each(function(f){
41672             if (!f.getName()) {
41673                 return;
41674             }
41675             var v = f.getValue();
41676             // not sure if this supported any more..
41677             if ((typeof(v) == 'object') && f.getRawValue) {
41678                 v = f.getRawValue() ; // dates..
41679             }
41680             // combo boxes where name != hiddenName...
41681             if (f.name != f.getName()) {
41682                 ret[f.name] = f.getRawValue();
41683             }
41684             ret[f.getName()] = v;
41685         });
41686         
41687         return ret;
41688     },
41689
41690     /**
41691      * Clears all invalid messages in this form.
41692      * @return {BasicForm} this
41693      */
41694     clearInvalid : function(){
41695         this.items.each(function(f){
41696            f.clearInvalid();
41697         });
41698         
41699         Roo.each(this.childForms || [], function (f) {
41700             f.clearInvalid();
41701         });
41702         
41703         
41704         return this;
41705     },
41706
41707     /**
41708      * Resets this form.
41709      * @return {BasicForm} this
41710      */
41711     reset : function(){
41712         this.items.each(function(f){
41713             f.reset();
41714         });
41715         
41716         Roo.each(this.childForms || [], function (f) {
41717             f.reset();
41718         });
41719        
41720         
41721         return this;
41722     },
41723
41724     /**
41725      * Add Roo.form components to this form.
41726      * @param {Field} field1
41727      * @param {Field} field2 (optional)
41728      * @param {Field} etc (optional)
41729      * @return {BasicForm} this
41730      */
41731     add : function(){
41732         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41733         return this;
41734     },
41735
41736
41737     /**
41738      * Removes a field from the items collection (does NOT remove its markup).
41739      * @param {Field} field
41740      * @return {BasicForm} this
41741      */
41742     remove : function(field){
41743         this.items.remove(field);
41744         return this;
41745     },
41746
41747     /**
41748      * Looks at the fields in this form, checks them for an id attribute,
41749      * and calls applyTo on the existing dom element with that id.
41750      * @return {BasicForm} this
41751      */
41752     render : function(){
41753         this.items.each(function(f){
41754             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41755                 f.applyTo(f.id);
41756             }
41757         });
41758         return this;
41759     },
41760
41761     /**
41762      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41763      * @param {Object} values
41764      * @return {BasicForm} this
41765      */
41766     applyToFields : function(o){
41767         this.items.each(function(f){
41768            Roo.apply(f, o);
41769         });
41770         return this;
41771     },
41772
41773     /**
41774      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41775      * @param {Object} values
41776      * @return {BasicForm} this
41777      */
41778     applyIfToFields : function(o){
41779         this.items.each(function(f){
41780            Roo.applyIf(f, o);
41781         });
41782         return this;
41783     }
41784 });
41785
41786 // back compat
41787 Roo.BasicForm = Roo.form.BasicForm;/*
41788  * Based on:
41789  * Ext JS Library 1.1.1
41790  * Copyright(c) 2006-2007, Ext JS, LLC.
41791  *
41792  * Originally Released Under LGPL - original licence link has changed is not relivant.
41793  *
41794  * Fork - LGPL
41795  * <script type="text/javascript">
41796  */
41797
41798 /**
41799  * @class Roo.form.Form
41800  * @extends Roo.form.BasicForm
41801  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41802  * @constructor
41803  * @param {Object} config Configuration options
41804  */
41805 Roo.form.Form = function(config){
41806     var xitems =  [];
41807     if (config.items) {
41808         xitems = config.items;
41809         delete config.items;
41810     }
41811    
41812     
41813     Roo.form.Form.superclass.constructor.call(this, null, config);
41814     this.url = this.url || this.action;
41815     if(!this.root){
41816         this.root = new Roo.form.Layout(Roo.applyIf({
41817             id: Roo.id()
41818         }, config));
41819     }
41820     this.active = this.root;
41821     /**
41822      * Array of all the buttons that have been added to this form via {@link addButton}
41823      * @type Array
41824      */
41825     this.buttons = [];
41826     this.allItems = [];
41827     this.addEvents({
41828         /**
41829          * @event clientvalidation
41830          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41831          * @param {Form} this
41832          * @param {Boolean} valid true if the form has passed client-side validation
41833          */
41834         clientvalidation: true,
41835         /**
41836          * @event rendered
41837          * Fires when the form is rendered
41838          * @param {Roo.form.Form} form
41839          */
41840         rendered : true
41841     });
41842     
41843     if (this.progressUrl) {
41844             // push a hidden field onto the list of fields..
41845             this.addxtype( {
41846                     xns: Roo.form, 
41847                     xtype : 'Hidden', 
41848                     name : 'UPLOAD_IDENTIFIER' 
41849             });
41850         }
41851         
41852     
41853     Roo.each(xitems, this.addxtype, this);
41854     
41855     
41856     
41857 };
41858
41859 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41860     /**
41861      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41862      */
41863     /**
41864      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41865      */
41866     /**
41867      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41868      */
41869     buttonAlign:'center',
41870
41871     /**
41872      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41873      */
41874     minButtonWidth:75,
41875
41876     /**
41877      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41878      * This property cascades to child containers if not set.
41879      */
41880     labelAlign:'left',
41881
41882     /**
41883      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41884      * fires a looping event with that state. This is required to bind buttons to the valid
41885      * state using the config value formBind:true on the button.
41886      */
41887     monitorValid : false,
41888
41889     /**
41890      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41891      */
41892     monitorPoll : 200,
41893     
41894     /**
41895      * @cfg {String} progressUrl - Url to return progress data 
41896      */
41897     
41898     progressUrl : false,
41899   
41900     /**
41901      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41902      * fields are added and the column is closed. If no fields are passed the column remains open
41903      * until end() is called.
41904      * @param {Object} config The config to pass to the column
41905      * @param {Field} field1 (optional)
41906      * @param {Field} field2 (optional)
41907      * @param {Field} etc (optional)
41908      * @return Column The column container object
41909      */
41910     column : function(c){
41911         var col = new Roo.form.Column(c);
41912         this.start(col);
41913         if(arguments.length > 1){ // duplicate code required because of Opera
41914             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41915             this.end();
41916         }
41917         return col;
41918     },
41919
41920     /**
41921      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41922      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41923      * until end() is called.
41924      * @param {Object} config The config to pass to the fieldset
41925      * @param {Field} field1 (optional)
41926      * @param {Field} field2 (optional)
41927      * @param {Field} etc (optional)
41928      * @return FieldSet The fieldset container object
41929      */
41930     fieldset : function(c){
41931         var fs = new Roo.form.FieldSet(c);
41932         this.start(fs);
41933         if(arguments.length > 1){ // duplicate code required because of Opera
41934             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41935             this.end();
41936         }
41937         return fs;
41938     },
41939
41940     /**
41941      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41942      * fields are added and the container is closed. If no fields are passed the container remains open
41943      * until end() is called.
41944      * @param {Object} config The config to pass to the Layout
41945      * @param {Field} field1 (optional)
41946      * @param {Field} field2 (optional)
41947      * @param {Field} etc (optional)
41948      * @return Layout The container object
41949      */
41950     container : function(c){
41951         var l = new Roo.form.Layout(c);
41952         this.start(l);
41953         if(arguments.length > 1){ // duplicate code required because of Opera
41954             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41955             this.end();
41956         }
41957         return l;
41958     },
41959
41960     /**
41961      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41962      * @param {Object} container A Roo.form.Layout or subclass of Layout
41963      * @return {Form} this
41964      */
41965     start : function(c){
41966         // cascade label info
41967         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41968         this.active.stack.push(c);
41969         c.ownerCt = this.active;
41970         this.active = c;
41971         return this;
41972     },
41973
41974     /**
41975      * Closes the current open container
41976      * @return {Form} this
41977      */
41978     end : function(){
41979         if(this.active == this.root){
41980             return this;
41981         }
41982         this.active = this.active.ownerCt;
41983         return this;
41984     },
41985
41986     /**
41987      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41988      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41989      * as the label of the field.
41990      * @param {Field} field1
41991      * @param {Field} field2 (optional)
41992      * @param {Field} etc. (optional)
41993      * @return {Form} this
41994      */
41995     add : function(){
41996         this.active.stack.push.apply(this.active.stack, arguments);
41997         this.allItems.push.apply(this.allItems,arguments);
41998         var r = [];
41999         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
42000             if(a[i].isFormField){
42001                 r.push(a[i]);
42002             }
42003         }
42004         if(r.length > 0){
42005             Roo.form.Form.superclass.add.apply(this, r);
42006         }
42007         return this;
42008     },
42009     
42010
42011     
42012     
42013     
42014      /**
42015      * Find any element that has been added to a form, using it's ID or name
42016      * This can include framesets, columns etc. along with regular fields..
42017      * @param {String} id - id or name to find.
42018      
42019      * @return {Element} e - or false if nothing found.
42020      */
42021     findbyId : function(id)
42022     {
42023         var ret = false;
42024         if (!id) {
42025             return ret;
42026         }
42027         Roo.each(this.allItems, function(f){
42028             if (f.id == id || f.name == id ){
42029                 ret = f;
42030                 return false;
42031             }
42032         });
42033         return ret;
42034     },
42035
42036     
42037     
42038     /**
42039      * Render this form into the passed container. This should only be called once!
42040      * @param {String/HTMLElement/Element} container The element this component should be rendered into
42041      * @return {Form} this
42042      */
42043     render : function(ct)
42044     {
42045         
42046         
42047         
42048         ct = Roo.get(ct);
42049         var o = this.autoCreate || {
42050             tag: 'form',
42051             method : this.method || 'POST',
42052             id : this.id || Roo.id()
42053         };
42054         this.initEl(ct.createChild(o));
42055
42056         this.root.render(this.el);
42057         
42058        
42059              
42060         this.items.each(function(f){
42061             f.render('x-form-el-'+f.id);
42062         });
42063
42064         if(this.buttons.length > 0){
42065             // tables are required to maintain order and for correct IE layout
42066             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
42067                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
42068                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
42069             }}, null, true);
42070             var tr = tb.getElementsByTagName('tr')[0];
42071             for(var i = 0, len = this.buttons.length; i < len; i++) {
42072                 var b = this.buttons[i];
42073                 var td = document.createElement('td');
42074                 td.className = 'x-form-btn-td';
42075                 b.render(tr.appendChild(td));
42076             }
42077         }
42078         if(this.monitorValid){ // initialize after render
42079             this.startMonitoring();
42080         }
42081         this.fireEvent('rendered', this);
42082         return this;
42083     },
42084
42085     /**
42086      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
42087      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
42088      * object or a valid Roo.DomHelper element config
42089      * @param {Function} handler The function called when the button is clicked
42090      * @param {Object} scope (optional) The scope of the handler function
42091      * @return {Roo.Button}
42092      */
42093     addButton : function(config, handler, scope){
42094         var bc = {
42095             handler: handler,
42096             scope: scope,
42097             minWidth: this.minButtonWidth,
42098             hideParent:true
42099         };
42100         if(typeof config == "string"){
42101             bc.text = config;
42102         }else{
42103             Roo.apply(bc, config);
42104         }
42105         var btn = new Roo.Button(null, bc);
42106         this.buttons.push(btn);
42107         return btn;
42108     },
42109
42110      /**
42111      * Adds a series of form elements (using the xtype property as the factory method.
42112      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
42113      * @param {Object} config 
42114      */
42115     
42116     addxtype : function()
42117     {
42118         var ar = Array.prototype.slice.call(arguments, 0);
42119         var ret = false;
42120         for(var i = 0; i < ar.length; i++) {
42121             if (!ar[i]) {
42122                 continue; // skip -- if this happends something invalid got sent, we 
42123                 // should ignore it, as basically that interface element will not show up
42124                 // and that should be pretty obvious!!
42125             }
42126             
42127             if (Roo.form[ar[i].xtype]) {
42128                 ar[i].form = this;
42129                 var fe = Roo.factory(ar[i], Roo.form);
42130                 if (!ret) {
42131                     ret = fe;
42132                 }
42133                 fe.form = this;
42134                 if (fe.store) {
42135                     fe.store.form = this;
42136                 }
42137                 if (fe.isLayout) {  
42138                          
42139                     this.start(fe);
42140                     this.allItems.push(fe);
42141                     if (fe.items && fe.addxtype) {
42142                         fe.addxtype.apply(fe, fe.items);
42143                         delete fe.items;
42144                     }
42145                      this.end();
42146                     continue;
42147                 }
42148                 
42149                 
42150                  
42151                 this.add(fe);
42152               //  console.log('adding ' + ar[i].xtype);
42153             }
42154             if (ar[i].xtype == 'Button') {  
42155                 //console.log('adding button');
42156                 //console.log(ar[i]);
42157                 this.addButton(ar[i]);
42158                 this.allItems.push(fe);
42159                 continue;
42160             }
42161             
42162             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42163                 alert('end is not supported on xtype any more, use items');
42164             //    this.end();
42165             //    //console.log('adding end');
42166             }
42167             
42168         }
42169         return ret;
42170     },
42171     
42172     /**
42173      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42174      * option "monitorValid"
42175      */
42176     startMonitoring : function(){
42177         if(!this.bound){
42178             this.bound = true;
42179             Roo.TaskMgr.start({
42180                 run : this.bindHandler,
42181                 interval : this.monitorPoll || 200,
42182                 scope: this
42183             });
42184         }
42185     },
42186
42187     /**
42188      * Stops monitoring of the valid state of this form
42189      */
42190     stopMonitoring : function(){
42191         this.bound = false;
42192     },
42193
42194     // private
42195     bindHandler : function(){
42196         if(!this.bound){
42197             return false; // stops binding
42198         }
42199         var valid = true;
42200         this.items.each(function(f){
42201             if(!f.isValid(true)){
42202                 valid = false;
42203                 return false;
42204             }
42205         });
42206         for(var i = 0, len = this.buttons.length; i < len; i++){
42207             var btn = this.buttons[i];
42208             if(btn.formBind === true && btn.disabled === valid){
42209                 btn.setDisabled(!valid);
42210             }
42211         }
42212         this.fireEvent('clientvalidation', this, valid);
42213     }
42214     
42215     
42216     
42217     
42218     
42219     
42220     
42221     
42222 });
42223
42224
42225 // back compat
42226 Roo.Form = Roo.form.Form;
42227 /*
42228  * Based on:
42229  * Ext JS Library 1.1.1
42230  * Copyright(c) 2006-2007, Ext JS, LLC.
42231  *
42232  * Originally Released Under LGPL - original licence link has changed is not relivant.
42233  *
42234  * Fork - LGPL
42235  * <script type="text/javascript">
42236  */
42237  
42238  /**
42239  * @class Roo.form.Action
42240  * Internal Class used to handle form actions
42241  * @constructor
42242  * @param {Roo.form.BasicForm} el The form element or its id
42243  * @param {Object} config Configuration options
42244  */
42245  
42246  
42247 // define the action interface
42248 Roo.form.Action = function(form, options){
42249     this.form = form;
42250     this.options = options || {};
42251 };
42252 /**
42253  * Client Validation Failed
42254  * @const 
42255  */
42256 Roo.form.Action.CLIENT_INVALID = 'client';
42257 /**
42258  * Server Validation Failed
42259  * @const 
42260  */
42261  Roo.form.Action.SERVER_INVALID = 'server';
42262  /**
42263  * Connect to Server Failed
42264  * @const 
42265  */
42266 Roo.form.Action.CONNECT_FAILURE = 'connect';
42267 /**
42268  * Reading Data from Server Failed
42269  * @const 
42270  */
42271 Roo.form.Action.LOAD_FAILURE = 'load';
42272
42273 Roo.form.Action.prototype = {
42274     type : 'default',
42275     failureType : undefined,
42276     response : undefined,
42277     result : undefined,
42278
42279     // interface method
42280     run : function(options){
42281
42282     },
42283
42284     // interface method
42285     success : function(response){
42286
42287     },
42288
42289     // interface method
42290     handleResponse : function(response){
42291
42292     },
42293
42294     // default connection failure
42295     failure : function(response){
42296         
42297         this.response = response;
42298         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42299         this.form.afterAction(this, false);
42300     },
42301
42302     processResponse : function(response){
42303         this.response = response;
42304         if(!response.responseText){
42305             return true;
42306         }
42307         this.result = this.handleResponse(response);
42308         return this.result;
42309     },
42310
42311     // utility functions used internally
42312     getUrl : function(appendParams){
42313         var url = this.options.url || this.form.url || this.form.el.dom.action;
42314         if(appendParams){
42315             var p = this.getParams();
42316             if(p){
42317                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42318             }
42319         }
42320         return url;
42321     },
42322
42323     getMethod : function(){
42324         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42325     },
42326
42327     getParams : function(){
42328         var bp = this.form.baseParams;
42329         var p = this.options.params;
42330         if(p){
42331             if(typeof p == "object"){
42332                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42333             }else if(typeof p == 'string' && bp){
42334                 p += '&' + Roo.urlEncode(bp);
42335             }
42336         }else if(bp){
42337             p = Roo.urlEncode(bp);
42338         }
42339         return p;
42340     },
42341
42342     createCallback : function(){
42343         return {
42344             success: this.success,
42345             failure: this.failure,
42346             scope: this,
42347             timeout: (this.form.timeout*1000),
42348             upload: this.form.fileUpload ? this.success : undefined
42349         };
42350     }
42351 };
42352
42353 Roo.form.Action.Submit = function(form, options){
42354     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42355 };
42356
42357 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42358     type : 'submit',
42359
42360     haveProgress : false,
42361     uploadComplete : false,
42362     
42363     // uploadProgress indicator.
42364     uploadProgress : function()
42365     {
42366         if (!this.form.progressUrl) {
42367             return;
42368         }
42369         
42370         if (!this.haveProgress) {
42371             Roo.MessageBox.progress("Uploading", "Uploading");
42372         }
42373         if (this.uploadComplete) {
42374            Roo.MessageBox.hide();
42375            return;
42376         }
42377         
42378         this.haveProgress = true;
42379    
42380         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42381         
42382         var c = new Roo.data.Connection();
42383         c.request({
42384             url : this.form.progressUrl,
42385             params: {
42386                 id : uid
42387             },
42388             method: 'GET',
42389             success : function(req){
42390                //console.log(data);
42391                 var rdata = false;
42392                 var edata;
42393                 try  {
42394                    rdata = Roo.decode(req.responseText)
42395                 } catch (e) {
42396                     Roo.log("Invalid data from server..");
42397                     Roo.log(edata);
42398                     return;
42399                 }
42400                 if (!rdata || !rdata.success) {
42401                     Roo.log(rdata);
42402                     return;
42403                 }
42404                 var data = rdata.data;
42405                 
42406                 if (this.uploadComplete) {
42407                    Roo.MessageBox.hide();
42408                    return;
42409                 }
42410                    
42411                 if (data){
42412                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42413                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42414                     );
42415                 }
42416                 this.uploadProgress.defer(2000,this);
42417             },
42418        
42419             failure: function(data) {
42420                 Roo.log('progress url failed ');
42421                 Roo.log(data);
42422             },
42423             scope : this
42424         });
42425            
42426     },
42427     
42428     
42429     run : function()
42430     {
42431         // run get Values on the form, so it syncs any secondary forms.
42432         this.form.getValues();
42433         
42434         var o = this.options;
42435         var method = this.getMethod();
42436         var isPost = method == 'POST';
42437         if(o.clientValidation === false || this.form.isValid()){
42438             
42439             if (this.form.progressUrl) {
42440                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42441                     (new Date() * 1) + '' + Math.random());
42442                     
42443             } 
42444             
42445             
42446             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42447                 form:this.form.el.dom,
42448                 url:this.getUrl(!isPost),
42449                 method: method,
42450                 params:isPost ? this.getParams() : null,
42451                 isUpload: this.form.fileUpload
42452             }));
42453             
42454             this.uploadProgress();
42455
42456         }else if (o.clientValidation !== false){ // client validation failed
42457             this.failureType = Roo.form.Action.CLIENT_INVALID;
42458             this.form.afterAction(this, false);
42459         }
42460     },
42461
42462     success : function(response)
42463     {
42464         this.uploadComplete= true;
42465         if (this.haveProgress) {
42466             Roo.MessageBox.hide();
42467         }
42468         
42469         
42470         var result = this.processResponse(response);
42471         if(result === true || result.success){
42472             this.form.afterAction(this, true);
42473             return;
42474         }
42475         if(result.errors){
42476             this.form.markInvalid(result.errors);
42477             this.failureType = Roo.form.Action.SERVER_INVALID;
42478         }
42479         this.form.afterAction(this, false);
42480     },
42481     failure : function(response)
42482     {
42483         this.uploadComplete= true;
42484         if (this.haveProgress) {
42485             Roo.MessageBox.hide();
42486         }
42487         
42488         this.response = response;
42489         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42490         this.form.afterAction(this, false);
42491     },
42492     
42493     handleResponse : function(response){
42494         if(this.form.errorReader){
42495             var rs = this.form.errorReader.read(response);
42496             var errors = [];
42497             if(rs.records){
42498                 for(var i = 0, len = rs.records.length; i < len; i++) {
42499                     var r = rs.records[i];
42500                     errors[i] = r.data;
42501                 }
42502             }
42503             if(errors.length < 1){
42504                 errors = null;
42505             }
42506             return {
42507                 success : rs.success,
42508                 errors : errors
42509             };
42510         }
42511         var ret = false;
42512         try {
42513             ret = Roo.decode(response.responseText);
42514         } catch (e) {
42515             ret = {
42516                 success: false,
42517                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42518                 errors : []
42519             };
42520         }
42521         return ret;
42522         
42523     }
42524 });
42525
42526
42527 Roo.form.Action.Load = function(form, options){
42528     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42529     this.reader = this.form.reader;
42530 };
42531
42532 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42533     type : 'load',
42534
42535     run : function(){
42536         
42537         Roo.Ajax.request(Roo.apply(
42538                 this.createCallback(), {
42539                     method:this.getMethod(),
42540                     url:this.getUrl(false),
42541                     params:this.getParams()
42542         }));
42543     },
42544
42545     success : function(response){
42546         
42547         var result = this.processResponse(response);
42548         if(result === true || !result.success || !result.data){
42549             this.failureType = Roo.form.Action.LOAD_FAILURE;
42550             this.form.afterAction(this, false);
42551             return;
42552         }
42553         this.form.clearInvalid();
42554         this.form.setValues(result.data);
42555         this.form.afterAction(this, true);
42556     },
42557
42558     handleResponse : function(response){
42559         if(this.form.reader){
42560             var rs = this.form.reader.read(response);
42561             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42562             return {
42563                 success : rs.success,
42564                 data : data
42565             };
42566         }
42567         return Roo.decode(response.responseText);
42568     }
42569 });
42570
42571 Roo.form.Action.ACTION_TYPES = {
42572     'load' : Roo.form.Action.Load,
42573     'submit' : Roo.form.Action.Submit
42574 };/*
42575  * Based on:
42576  * Ext JS Library 1.1.1
42577  * Copyright(c) 2006-2007, Ext JS, LLC.
42578  *
42579  * Originally Released Under LGPL - original licence link has changed is not relivant.
42580  *
42581  * Fork - LGPL
42582  * <script type="text/javascript">
42583  */
42584  
42585 /**
42586  * @class Roo.form.Layout
42587  * @extends Roo.Component
42588  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42589  * @constructor
42590  * @param {Object} config Configuration options
42591  */
42592 Roo.form.Layout = function(config){
42593     var xitems = [];
42594     if (config.items) {
42595         xitems = config.items;
42596         delete config.items;
42597     }
42598     Roo.form.Layout.superclass.constructor.call(this, config);
42599     this.stack = [];
42600     Roo.each(xitems, this.addxtype, this);
42601      
42602 };
42603
42604 Roo.extend(Roo.form.Layout, Roo.Component, {
42605     /**
42606      * @cfg {String/Object} autoCreate
42607      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42608      */
42609     /**
42610      * @cfg {String/Object/Function} style
42611      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42612      * a function which returns such a specification.
42613      */
42614     /**
42615      * @cfg {String} labelAlign
42616      * Valid values are "left," "top" and "right" (defaults to "left")
42617      */
42618     /**
42619      * @cfg {Number} labelWidth
42620      * Fixed width in pixels of all field labels (defaults to undefined)
42621      */
42622     /**
42623      * @cfg {Boolean} clear
42624      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42625      */
42626     clear : true,
42627     /**
42628      * @cfg {String} labelSeparator
42629      * The separator to use after field labels (defaults to ':')
42630      */
42631     labelSeparator : ':',
42632     /**
42633      * @cfg {Boolean} hideLabels
42634      * True to suppress the display of field labels in this layout (defaults to false)
42635      */
42636     hideLabels : false,
42637
42638     // private
42639     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42640     
42641     isLayout : true,
42642     
42643     // private
42644     onRender : function(ct, position){
42645         if(this.el){ // from markup
42646             this.el = Roo.get(this.el);
42647         }else {  // generate
42648             var cfg = this.getAutoCreate();
42649             this.el = ct.createChild(cfg, position);
42650         }
42651         if(this.style){
42652             this.el.applyStyles(this.style);
42653         }
42654         if(this.labelAlign){
42655             this.el.addClass('x-form-label-'+this.labelAlign);
42656         }
42657         if(this.hideLabels){
42658             this.labelStyle = "display:none";
42659             this.elementStyle = "padding-left:0;";
42660         }else{
42661             if(typeof this.labelWidth == 'number'){
42662                 this.labelStyle = "width:"+this.labelWidth+"px;";
42663                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42664             }
42665             if(this.labelAlign == 'top'){
42666                 this.labelStyle = "width:auto;";
42667                 this.elementStyle = "padding-left:0;";
42668             }
42669         }
42670         var stack = this.stack;
42671         var slen = stack.length;
42672         if(slen > 0){
42673             if(!this.fieldTpl){
42674                 var t = new Roo.Template(
42675                     '<div class="x-form-item {5}">',
42676                         '<label for="{0}" style="{2}">{1}{4}</label>',
42677                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42678                         '</div>',
42679                     '</div><div class="x-form-clear-left"></div>'
42680                 );
42681                 t.disableFormats = true;
42682                 t.compile();
42683                 Roo.form.Layout.prototype.fieldTpl = t;
42684             }
42685             for(var i = 0; i < slen; i++) {
42686                 if(stack[i].isFormField){
42687                     this.renderField(stack[i]);
42688                 }else{
42689                     this.renderComponent(stack[i]);
42690                 }
42691             }
42692         }
42693         if(this.clear){
42694             this.el.createChild({cls:'x-form-clear'});
42695         }
42696     },
42697
42698     // private
42699     renderField : function(f){
42700         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42701                f.id, //0
42702                f.fieldLabel, //1
42703                f.labelStyle||this.labelStyle||'', //2
42704                this.elementStyle||'', //3
42705                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42706                f.itemCls||this.itemCls||''  //5
42707        ], true).getPrevSibling());
42708     },
42709
42710     // private
42711     renderComponent : function(c){
42712         c.render(c.isLayout ? this.el : this.el.createChild());    
42713     },
42714     /**
42715      * Adds a object form elements (using the xtype property as the factory method.)
42716      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42717      * @param {Object} config 
42718      */
42719     addxtype : function(o)
42720     {
42721         // create the lement.
42722         o.form = this.form;
42723         var fe = Roo.factory(o, Roo.form);
42724         this.form.allItems.push(fe);
42725         this.stack.push(fe);
42726         
42727         if (fe.isFormField) {
42728             this.form.items.add(fe);
42729         }
42730          
42731         return fe;
42732     }
42733 });
42734
42735 /**
42736  * @class Roo.form.Column
42737  * @extends Roo.form.Layout
42738  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42739  * @constructor
42740  * @param {Object} config Configuration options
42741  */
42742 Roo.form.Column = function(config){
42743     Roo.form.Column.superclass.constructor.call(this, config);
42744 };
42745
42746 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42747     /**
42748      * @cfg {Number/String} width
42749      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42750      */
42751     /**
42752      * @cfg {String/Object} autoCreate
42753      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42754      */
42755
42756     // private
42757     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42758
42759     // private
42760     onRender : function(ct, position){
42761         Roo.form.Column.superclass.onRender.call(this, ct, position);
42762         if(this.width){
42763             this.el.setWidth(this.width);
42764         }
42765     }
42766 });
42767
42768
42769 /**
42770  * @class Roo.form.Row
42771  * @extends Roo.form.Layout
42772  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42773  * @constructor
42774  * @param {Object} config Configuration options
42775  */
42776
42777  
42778 Roo.form.Row = function(config){
42779     Roo.form.Row.superclass.constructor.call(this, config);
42780 };
42781  
42782 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42783       /**
42784      * @cfg {Number/String} width
42785      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42786      */
42787     /**
42788      * @cfg {Number/String} height
42789      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42790      */
42791     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42792     
42793     padWidth : 20,
42794     // private
42795     onRender : function(ct, position){
42796         //console.log('row render');
42797         if(!this.rowTpl){
42798             var t = new Roo.Template(
42799                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42800                     '<label for="{0}" style="{2}">{1}{4}</label>',
42801                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42802                     '</div>',
42803                 '</div>'
42804             );
42805             t.disableFormats = true;
42806             t.compile();
42807             Roo.form.Layout.prototype.rowTpl = t;
42808         }
42809         this.fieldTpl = this.rowTpl;
42810         
42811         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42812         var labelWidth = 100;
42813         
42814         if ((this.labelAlign != 'top')) {
42815             if (typeof this.labelWidth == 'number') {
42816                 labelWidth = this.labelWidth
42817             }
42818             this.padWidth =  20 + labelWidth;
42819             
42820         }
42821         
42822         Roo.form.Column.superclass.onRender.call(this, ct, position);
42823         if(this.width){
42824             this.el.setWidth(this.width);
42825         }
42826         if(this.height){
42827             this.el.setHeight(this.height);
42828         }
42829     },
42830     
42831     // private
42832     renderField : function(f){
42833         f.fieldEl = this.fieldTpl.append(this.el, [
42834                f.id, f.fieldLabel,
42835                f.labelStyle||this.labelStyle||'',
42836                this.elementStyle||'',
42837                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42838                f.itemCls||this.itemCls||'',
42839                f.width ? f.width + this.padWidth : 160 + this.padWidth
42840        ],true);
42841     }
42842 });
42843  
42844
42845 /**
42846  * @class Roo.form.FieldSet
42847  * @extends Roo.form.Layout
42848  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42849  * @constructor
42850  * @param {Object} config Configuration options
42851  */
42852 Roo.form.FieldSet = function(config){
42853     Roo.form.FieldSet.superclass.constructor.call(this, config);
42854 };
42855
42856 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42857     /**
42858      * @cfg {String} legend
42859      * The text to display as the legend for the FieldSet (defaults to '')
42860      */
42861     /**
42862      * @cfg {String/Object} autoCreate
42863      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42864      */
42865
42866     // private
42867     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42868
42869     // private
42870     onRender : function(ct, position){
42871         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42872         if(this.legend){
42873             this.setLegend(this.legend);
42874         }
42875     },
42876
42877     // private
42878     setLegend : function(text){
42879         if(this.rendered){
42880             this.el.child('legend').update(text);
42881         }
42882     }
42883 });/*
42884  * Based on:
42885  * Ext JS Library 1.1.1
42886  * Copyright(c) 2006-2007, Ext JS, LLC.
42887  *
42888  * Originally Released Under LGPL - original licence link has changed is not relivant.
42889  *
42890  * Fork - LGPL
42891  * <script type="text/javascript">
42892  */
42893 /**
42894  * @class Roo.form.VTypes
42895  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42896  * @singleton
42897  */
42898 Roo.form.VTypes = function(){
42899     // closure these in so they are only created once.
42900     var alpha = /^[a-zA-Z_]+$/;
42901     var alphanum = /^[a-zA-Z0-9_]+$/;
42902     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42903     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42904
42905     // All these messages and functions are configurable
42906     return {
42907         /**
42908          * The function used to validate email addresses
42909          * @param {String} value The email address
42910          */
42911         'email' : function(v){
42912             return email.test(v);
42913         },
42914         /**
42915          * The error text to display when the email validation function returns false
42916          * @type String
42917          */
42918         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42919         /**
42920          * The keystroke filter mask to be applied on email input
42921          * @type RegExp
42922          */
42923         'emailMask' : /[a-z0-9_\.\-@]/i,
42924
42925         /**
42926          * The function used to validate URLs
42927          * @param {String} value The URL
42928          */
42929         'url' : function(v){
42930             return url.test(v);
42931         },
42932         /**
42933          * The error text to display when the url validation function returns false
42934          * @type String
42935          */
42936         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42937         
42938         /**
42939          * The function used to validate alpha values
42940          * @param {String} value The value
42941          */
42942         'alpha' : function(v){
42943             return alpha.test(v);
42944         },
42945         /**
42946          * The error text to display when the alpha validation function returns false
42947          * @type String
42948          */
42949         'alphaText' : 'This field should only contain letters and _',
42950         /**
42951          * The keystroke filter mask to be applied on alpha input
42952          * @type RegExp
42953          */
42954         'alphaMask' : /[a-z_]/i,
42955
42956         /**
42957          * The function used to validate alphanumeric values
42958          * @param {String} value The value
42959          */
42960         'alphanum' : function(v){
42961             return alphanum.test(v);
42962         },
42963         /**
42964          * The error text to display when the alphanumeric validation function returns false
42965          * @type String
42966          */
42967         'alphanumText' : 'This field should only contain letters, numbers and _',
42968         /**
42969          * The keystroke filter mask to be applied on alphanumeric input
42970          * @type RegExp
42971          */
42972         'alphanumMask' : /[a-z0-9_]/i
42973     };
42974 }();//<script type="text/javascript">
42975
42976 /**
42977  * @class Roo.form.FCKeditor
42978  * @extends Roo.form.TextArea
42979  * Wrapper around the FCKEditor http://www.fckeditor.net
42980  * @constructor
42981  * Creates a new FCKeditor
42982  * @param {Object} config Configuration options
42983  */
42984 Roo.form.FCKeditor = function(config){
42985     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42986     this.addEvents({
42987          /**
42988          * @event editorinit
42989          * Fired when the editor is initialized - you can add extra handlers here..
42990          * @param {FCKeditor} this
42991          * @param {Object} the FCK object.
42992          */
42993         editorinit : true
42994     });
42995     
42996     
42997 };
42998 Roo.form.FCKeditor.editors = { };
42999 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
43000 {
43001     //defaultAutoCreate : {
43002     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
43003     //},
43004     // private
43005     /**
43006      * @cfg {Object} fck options - see fck manual for details.
43007      */
43008     fckconfig : false,
43009     
43010     /**
43011      * @cfg {Object} fck toolbar set (Basic or Default)
43012      */
43013     toolbarSet : 'Basic',
43014     /**
43015      * @cfg {Object} fck BasePath
43016      */ 
43017     basePath : '/fckeditor/',
43018     
43019     
43020     frame : false,
43021     
43022     value : '',
43023     
43024    
43025     onRender : function(ct, position)
43026     {
43027         if(!this.el){
43028             this.defaultAutoCreate = {
43029                 tag: "textarea",
43030                 style:"width:300px;height:60px;",
43031                 autocomplete: "off"
43032             };
43033         }
43034         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
43035         /*
43036         if(this.grow){
43037             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
43038             if(this.preventScrollbars){
43039                 this.el.setStyle("overflow", "hidden");
43040             }
43041             this.el.setHeight(this.growMin);
43042         }
43043         */
43044         //console.log('onrender' + this.getId() );
43045         Roo.form.FCKeditor.editors[this.getId()] = this;
43046          
43047
43048         this.replaceTextarea() ;
43049         
43050     },
43051     
43052     getEditor : function() {
43053         return this.fckEditor;
43054     },
43055     /**
43056      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
43057      * @param {Mixed} value The value to set
43058      */
43059     
43060     
43061     setValue : function(value)
43062     {
43063         //console.log('setValue: ' + value);
43064         
43065         if(typeof(value) == 'undefined') { // not sure why this is happending...
43066             return;
43067         }
43068         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43069         
43070         //if(!this.el || !this.getEditor()) {
43071         //    this.value = value;
43072             //this.setValue.defer(100,this,[value]);    
43073         //    return;
43074         //} 
43075         
43076         if(!this.getEditor()) {
43077             return;
43078         }
43079         
43080         this.getEditor().SetData(value);
43081         
43082         //
43083
43084     },
43085
43086     /**
43087      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
43088      * @return {Mixed} value The field value
43089      */
43090     getValue : function()
43091     {
43092         
43093         if (this.frame && this.frame.dom.style.display == 'none') {
43094             return Roo.form.FCKeditor.superclass.getValue.call(this);
43095         }
43096         
43097         if(!this.el || !this.getEditor()) {
43098            
43099            // this.getValue.defer(100,this); 
43100             return this.value;
43101         }
43102        
43103         
43104         var value=this.getEditor().GetData();
43105         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43106         return Roo.form.FCKeditor.superclass.getValue.call(this);
43107         
43108
43109     },
43110
43111     /**
43112      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
43113      * @return {Mixed} value The field value
43114      */
43115     getRawValue : function()
43116     {
43117         if (this.frame && this.frame.dom.style.display == 'none') {
43118             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43119         }
43120         
43121         if(!this.el || !this.getEditor()) {
43122             //this.getRawValue.defer(100,this); 
43123             return this.value;
43124             return;
43125         }
43126         
43127         
43128         
43129         var value=this.getEditor().GetData();
43130         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43131         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43132          
43133     },
43134     
43135     setSize : function(w,h) {
43136         
43137         
43138         
43139         //if (this.frame && this.frame.dom.style.display == 'none') {
43140         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43141         //    return;
43142         //}
43143         //if(!this.el || !this.getEditor()) {
43144         //    this.setSize.defer(100,this, [w,h]); 
43145         //    return;
43146         //}
43147         
43148         
43149         
43150         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43151         
43152         this.frame.dom.setAttribute('width', w);
43153         this.frame.dom.setAttribute('height', h);
43154         this.frame.setSize(w,h);
43155         
43156     },
43157     
43158     toggleSourceEdit : function(value) {
43159         
43160       
43161          
43162         this.el.dom.style.display = value ? '' : 'none';
43163         this.frame.dom.style.display = value ?  'none' : '';
43164         
43165     },
43166     
43167     
43168     focus: function(tag)
43169     {
43170         if (this.frame.dom.style.display == 'none') {
43171             return Roo.form.FCKeditor.superclass.focus.call(this);
43172         }
43173         if(!this.el || !this.getEditor()) {
43174             this.focus.defer(100,this, [tag]); 
43175             return;
43176         }
43177         
43178         
43179         
43180         
43181         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43182         this.getEditor().Focus();
43183         if (tgs.length) {
43184             if (!this.getEditor().Selection.GetSelection()) {
43185                 this.focus.defer(100,this, [tag]); 
43186                 return;
43187             }
43188             
43189             
43190             var r = this.getEditor().EditorDocument.createRange();
43191             r.setStart(tgs[0],0);
43192             r.setEnd(tgs[0],0);
43193             this.getEditor().Selection.GetSelection().removeAllRanges();
43194             this.getEditor().Selection.GetSelection().addRange(r);
43195             this.getEditor().Focus();
43196         }
43197         
43198     },
43199     
43200     
43201     
43202     replaceTextarea : function()
43203     {
43204         if ( document.getElementById( this.getId() + '___Frame' ) )
43205             return ;
43206         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43207         //{
43208             // We must check the elements firstly using the Id and then the name.
43209         var oTextarea = document.getElementById( this.getId() );
43210         
43211         var colElementsByName = document.getElementsByName( this.getId() ) ;
43212          
43213         oTextarea.style.display = 'none' ;
43214
43215         if ( oTextarea.tabIndex ) {            
43216             this.TabIndex = oTextarea.tabIndex ;
43217         }
43218         
43219         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43220         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43221         this.frame = Roo.get(this.getId() + '___Frame')
43222     },
43223     
43224     _getConfigHtml : function()
43225     {
43226         var sConfig = '' ;
43227
43228         for ( var o in this.fckconfig ) {
43229             sConfig += sConfig.length > 0  ? '&amp;' : '';
43230             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43231         }
43232
43233         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43234     },
43235     
43236     
43237     _getIFrameHtml : function()
43238     {
43239         var sFile = 'fckeditor.html' ;
43240         /* no idea what this is about..
43241         try
43242         {
43243             if ( (/fcksource=true/i).test( window.top.location.search ) )
43244                 sFile = 'fckeditor.original.html' ;
43245         }
43246         catch (e) { 
43247         */
43248
43249         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43250         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43251         
43252         
43253         var html = '<iframe id="' + this.getId() +
43254             '___Frame" src="' + sLink +
43255             '" width="' + this.width +
43256             '" height="' + this.height + '"' +
43257             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43258             ' frameborder="0" scrolling="no"></iframe>' ;
43259
43260         return html ;
43261     },
43262     
43263     _insertHtmlBefore : function( html, element )
43264     {
43265         if ( element.insertAdjacentHTML )       {
43266             // IE
43267             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43268         } else { // Gecko
43269             var oRange = document.createRange() ;
43270             oRange.setStartBefore( element ) ;
43271             var oFragment = oRange.createContextualFragment( html );
43272             element.parentNode.insertBefore( oFragment, element ) ;
43273         }
43274     }
43275     
43276     
43277   
43278     
43279     
43280     
43281     
43282
43283 });
43284
43285 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43286
43287 function FCKeditor_OnComplete(editorInstance){
43288     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43289     f.fckEditor = editorInstance;
43290     //console.log("loaded");
43291     f.fireEvent('editorinit', f, editorInstance);
43292
43293   
43294
43295  
43296
43297
43298
43299
43300
43301
43302
43303
43304
43305
43306
43307
43308
43309
43310
43311 //<script type="text/javascript">
43312 /**
43313  * @class Roo.form.GridField
43314  * @extends Roo.form.Field
43315  * Embed a grid (or editable grid into a form)
43316  * STATUS ALPHA
43317  * 
43318  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43319  * it needs 
43320  * xgrid.store = Roo.data.Store
43321  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43322  * xgrid.store.reader = Roo.data.JsonReader 
43323  * 
43324  * 
43325  * @constructor
43326  * Creates a new GridField
43327  * @param {Object} config Configuration options
43328  */
43329 Roo.form.GridField = function(config){
43330     Roo.form.GridField.superclass.constructor.call(this, config);
43331      
43332 };
43333
43334 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43335     /**
43336      * @cfg {Number} width  - used to restrict width of grid..
43337      */
43338     width : 100,
43339     /**
43340      * @cfg {Number} height - used to restrict height of grid..
43341      */
43342     height : 50,
43343      /**
43344      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43345          * 
43346          *}
43347      */
43348     xgrid : false, 
43349     /**
43350      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43351      * {tag: "input", type: "checkbox", autocomplete: "off"})
43352      */
43353    // defaultAutoCreate : { tag: 'div' },
43354     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43355     /**
43356      * @cfg {String} addTitle Text to include for adding a title.
43357      */
43358     addTitle : false,
43359     //
43360     onResize : function(){
43361         Roo.form.Field.superclass.onResize.apply(this, arguments);
43362     },
43363
43364     initEvents : function(){
43365         // Roo.form.Checkbox.superclass.initEvents.call(this);
43366         // has no events...
43367        
43368     },
43369
43370
43371     getResizeEl : function(){
43372         return this.wrap;
43373     },
43374
43375     getPositionEl : function(){
43376         return this.wrap;
43377     },
43378
43379     // private
43380     onRender : function(ct, position){
43381         
43382         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43383         var style = this.style;
43384         delete this.style;
43385         
43386         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43387         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43388         this.viewEl = this.wrap.createChild({ tag: 'div' });
43389         if (style) {
43390             this.viewEl.applyStyles(style);
43391         }
43392         if (this.width) {
43393             this.viewEl.setWidth(this.width);
43394         }
43395         if (this.height) {
43396             this.viewEl.setHeight(this.height);
43397         }
43398         //if(this.inputValue !== undefined){
43399         //this.setValue(this.value);
43400         
43401         
43402         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43403         
43404         
43405         this.grid.render();
43406         this.grid.getDataSource().on('remove', this.refreshValue, this);
43407         this.grid.getDataSource().on('update', this.refreshValue, this);
43408         this.grid.on('afteredit', this.refreshValue, this);
43409  
43410     },
43411      
43412     
43413     /**
43414      * Sets the value of the item. 
43415      * @param {String} either an object  or a string..
43416      */
43417     setValue : function(v){
43418         //this.value = v;
43419         v = v || []; // empty set..
43420         // this does not seem smart - it really only affects memoryproxy grids..
43421         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43422             var ds = this.grid.getDataSource();
43423             // assumes a json reader..
43424             var data = {}
43425             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43426             ds.loadData( data);
43427         }
43428         // clear selection so it does not get stale.
43429         if (this.grid.sm) { 
43430             this.grid.sm.clearSelections();
43431         }
43432         
43433         Roo.form.GridField.superclass.setValue.call(this, v);
43434         this.refreshValue();
43435         // should load data in the grid really....
43436     },
43437     
43438     // private
43439     refreshValue: function() {
43440          var val = [];
43441         this.grid.getDataSource().each(function(r) {
43442             val.push(r.data);
43443         });
43444         this.el.dom.value = Roo.encode(val);
43445     }
43446     
43447      
43448     
43449     
43450 });/*
43451  * Based on:
43452  * Ext JS Library 1.1.1
43453  * Copyright(c) 2006-2007, Ext JS, LLC.
43454  *
43455  * Originally Released Under LGPL - original licence link has changed is not relivant.
43456  *
43457  * Fork - LGPL
43458  * <script type="text/javascript">
43459  */
43460 /**
43461  * @class Roo.form.DisplayField
43462  * @extends Roo.form.Field
43463  * A generic Field to display non-editable data.
43464  * @constructor
43465  * Creates a new Display Field item.
43466  * @param {Object} config Configuration options
43467  */
43468 Roo.form.DisplayField = function(config){
43469     Roo.form.DisplayField.superclass.constructor.call(this, config);
43470     
43471 };
43472
43473 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43474     inputType:      'hidden',
43475     allowBlank:     true,
43476     readOnly:         true,
43477     
43478  
43479     /**
43480      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43481      */
43482     focusClass : undefined,
43483     /**
43484      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43485      */
43486     fieldClass: 'x-form-field',
43487     
43488      /**
43489      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43490      */
43491     valueRenderer: undefined,
43492     
43493     width: 100,
43494     /**
43495      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43496      * {tag: "input", type: "checkbox", autocomplete: "off"})
43497      */
43498      
43499  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43500
43501     onResize : function(){
43502         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43503         
43504     },
43505
43506     initEvents : function(){
43507         // Roo.form.Checkbox.superclass.initEvents.call(this);
43508         // has no events...
43509        
43510     },
43511
43512
43513     getResizeEl : function(){
43514         return this.wrap;
43515     },
43516
43517     getPositionEl : function(){
43518         return this.wrap;
43519     },
43520
43521     // private
43522     onRender : function(ct, position){
43523         
43524         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43525         //if(this.inputValue !== undefined){
43526         this.wrap = this.el.wrap();
43527         
43528         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43529         
43530         if (this.bodyStyle) {
43531             this.viewEl.applyStyles(this.bodyStyle);
43532         }
43533         //this.viewEl.setStyle('padding', '2px');
43534         
43535         this.setValue(this.value);
43536         
43537     },
43538 /*
43539     // private
43540     initValue : Roo.emptyFn,
43541
43542   */
43543
43544         // private
43545     onClick : function(){
43546         
43547     },
43548
43549     /**
43550      * Sets the checked state of the checkbox.
43551      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43552      */
43553     setValue : function(v){
43554         this.value = v;
43555         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43556         // this might be called before we have a dom element..
43557         if (!this.viewEl) {
43558             return;
43559         }
43560         this.viewEl.dom.innerHTML = html;
43561         Roo.form.DisplayField.superclass.setValue.call(this, v);
43562
43563     }
43564 });/*
43565  * 
43566  * Licence- LGPL
43567  * 
43568  */
43569
43570 /**
43571  * @class Roo.form.DayPicker
43572  * @extends Roo.form.Field
43573  * A Day picker show [M] [T] [W] ....
43574  * @constructor
43575  * Creates a new Day Picker
43576  * @param {Object} config Configuration options
43577  */
43578 Roo.form.DayPicker= function(config){
43579     Roo.form.DayPicker.superclass.constructor.call(this, config);
43580      
43581 };
43582
43583 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43584     /**
43585      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43586      */
43587     focusClass : undefined,
43588     /**
43589      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43590      */
43591     fieldClass: "x-form-field",
43592    
43593     /**
43594      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43595      * {tag: "input", type: "checkbox", autocomplete: "off"})
43596      */
43597     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43598     
43599    
43600     actionMode : 'viewEl', 
43601     //
43602     // private
43603  
43604     inputType : 'hidden',
43605     
43606      
43607     inputElement: false, // real input element?
43608     basedOn: false, // ????
43609     
43610     isFormField: true, // not sure where this is needed!!!!
43611
43612     onResize : function(){
43613         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43614         if(!this.boxLabel){
43615             this.el.alignTo(this.wrap, 'c-c');
43616         }
43617     },
43618
43619     initEvents : function(){
43620         Roo.form.Checkbox.superclass.initEvents.call(this);
43621         this.el.on("click", this.onClick,  this);
43622         this.el.on("change", this.onClick,  this);
43623     },
43624
43625
43626     getResizeEl : function(){
43627         return this.wrap;
43628     },
43629
43630     getPositionEl : function(){
43631         return this.wrap;
43632     },
43633
43634     
43635     // private
43636     onRender : function(ct, position){
43637         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43638        
43639         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43640         
43641         var r1 = '<table><tr>';
43642         var r2 = '<tr class="x-form-daypick-icons">';
43643         for (var i=0; i < 7; i++) {
43644             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43645             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43646         }
43647         
43648         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43649         viewEl.select('img').on('click', this.onClick, this);
43650         this.viewEl = viewEl;   
43651         
43652         
43653         // this will not work on Chrome!!!
43654         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43655         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43656         
43657         
43658           
43659
43660     },
43661
43662     // private
43663     initValue : Roo.emptyFn,
43664
43665     /**
43666      * Returns the checked state of the checkbox.
43667      * @return {Boolean} True if checked, else false
43668      */
43669     getValue : function(){
43670         return this.el.dom.value;
43671         
43672     },
43673
43674         // private
43675     onClick : function(e){ 
43676         //this.setChecked(!this.checked);
43677         Roo.get(e.target).toggleClass('x-menu-item-checked');
43678         this.refreshValue();
43679         //if(this.el.dom.checked != this.checked){
43680         //    this.setValue(this.el.dom.checked);
43681        // }
43682     },
43683     
43684     // private
43685     refreshValue : function()
43686     {
43687         var val = '';
43688         this.viewEl.select('img',true).each(function(e,i,n)  {
43689             val += e.is(".x-menu-item-checked") ? String(n) : '';
43690         });
43691         this.setValue(val, true);
43692     },
43693
43694     /**
43695      * Sets the checked state of the checkbox.
43696      * On is always based on a string comparison between inputValue and the param.
43697      * @param {Boolean/String} value - the value to set 
43698      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43699      */
43700     setValue : function(v,suppressEvent){
43701         if (!this.el.dom) {
43702             return;
43703         }
43704         var old = this.el.dom.value ;
43705         this.el.dom.value = v;
43706         if (suppressEvent) {
43707             return ;
43708         }
43709          
43710         // update display..
43711         this.viewEl.select('img',true).each(function(e,i,n)  {
43712             
43713             var on = e.is(".x-menu-item-checked");
43714             var newv = v.indexOf(String(n)) > -1;
43715             if (on != newv) {
43716                 e.toggleClass('x-menu-item-checked');
43717             }
43718             
43719         });
43720         
43721         
43722         this.fireEvent('change', this, v, old);
43723         
43724         
43725     },
43726    
43727     // handle setting of hidden value by some other method!!?!?
43728     setFromHidden: function()
43729     {
43730         if(!this.el){
43731             return;
43732         }
43733         //console.log("SET FROM HIDDEN");
43734         //alert('setFrom hidden');
43735         this.setValue(this.el.dom.value);
43736     },
43737     
43738     onDestroy : function()
43739     {
43740         if(this.viewEl){
43741             Roo.get(this.viewEl).remove();
43742         }
43743          
43744         Roo.form.DayPicker.superclass.onDestroy.call(this);
43745     }
43746
43747 });/*
43748  * RooJS Library 1.1.1
43749  * Copyright(c) 2008-2011  Alan Knowles
43750  *
43751  * License - LGPL
43752  */
43753  
43754
43755 /**
43756  * @class Roo.form.ComboCheck
43757  * @extends Roo.form.ComboBox
43758  * A combobox for multiple select items.
43759  *
43760  * FIXME - could do with a reset button..
43761  * 
43762  * @constructor
43763  * Create a new ComboCheck
43764  * @param {Object} config Configuration options
43765  */
43766 Roo.form.ComboCheck = function(config){
43767     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43768     // should verify some data...
43769     // like
43770     // hiddenName = required..
43771     // displayField = required
43772     // valudField == required
43773     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43774     var _t = this;
43775     Roo.each(req, function(e) {
43776         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43777             throw "Roo.form.ComboCheck : missing value for: " + e;
43778         }
43779     });
43780     
43781     
43782 };
43783
43784 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43785      
43786      
43787     editable : false,
43788      
43789     selectedClass: 'x-menu-item-checked', 
43790     
43791     // private
43792     onRender : function(ct, position){
43793         var _t = this;
43794         
43795         
43796         
43797         if(!this.tpl){
43798             var cls = 'x-combo-list';
43799
43800             
43801             this.tpl =  new Roo.Template({
43802                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43803                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43804                    '<span>{' + this.displayField + '}</span>' +
43805                     '</div>' 
43806                 
43807             });
43808         }
43809  
43810         
43811         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43812         this.view.singleSelect = false;
43813         this.view.multiSelect = true;
43814         this.view.toggleSelect = true;
43815         this.pageTb.add(new Roo.Toolbar.Fill(), {
43816             
43817             text: 'Done',
43818             handler: function()
43819             {
43820                 _t.collapse();
43821             }
43822         });
43823     },
43824     
43825     onViewOver : function(e, t){
43826         // do nothing...
43827         return;
43828         
43829     },
43830     
43831     onViewClick : function(doFocus,index){
43832         return;
43833         
43834     },
43835     select: function () {
43836         //Roo.log("SELECT CALLED");
43837     },
43838      
43839     selectByValue : function(xv, scrollIntoView){
43840         var ar = this.getValueArray();
43841         var sels = [];
43842         
43843         Roo.each(ar, function(v) {
43844             if(v === undefined || v === null){
43845                 return;
43846             }
43847             var r = this.findRecord(this.valueField, v);
43848             if(r){
43849                 sels.push(this.store.indexOf(r))
43850                 
43851             }
43852         },this);
43853         this.view.select(sels);
43854         return false;
43855     },
43856     
43857     
43858     
43859     onSelect : function(record, index){
43860        // Roo.log("onselect Called");
43861        // this is only called by the clear button now..
43862         this.view.clearSelections();
43863         this.setValue('[]');
43864         if (this.value != this.valueBefore) {
43865             this.fireEvent('change', this, this.value, this.valueBefore);
43866         }
43867     },
43868     getValueArray : function()
43869     {
43870         var ar = [] ;
43871         
43872         try {
43873             //Roo.log(this.value);
43874             if (typeof(this.value) == 'undefined') {
43875                 return [];
43876             }
43877             var ar = Roo.decode(this.value);
43878             return  ar instanceof Array ? ar : []; //?? valid?
43879             
43880         } catch(e) {
43881             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43882             return [];
43883         }
43884          
43885     },
43886     expand : function ()
43887     {
43888         Roo.form.ComboCheck.superclass.expand.call(this);
43889         this.valueBefore = this.value;
43890         
43891
43892     },
43893     
43894     collapse : function(){
43895         Roo.form.ComboCheck.superclass.collapse.call(this);
43896         var sl = this.view.getSelectedIndexes();
43897         var st = this.store;
43898         var nv = [];
43899         var tv = [];
43900         var r;
43901         Roo.each(sl, function(i) {
43902             r = st.getAt(i);
43903             nv.push(r.get(this.valueField));
43904         },this);
43905         this.setValue(Roo.encode(nv));
43906         if (this.value != this.valueBefore) {
43907
43908             this.fireEvent('change', this, this.value, this.valueBefore);
43909         }
43910         
43911     },
43912     
43913     setValue : function(v){
43914         // Roo.log(v);
43915         this.value = v;
43916         
43917         var vals = this.getValueArray();
43918         var tv = [];
43919         Roo.each(vals, function(k) {
43920             var r = this.findRecord(this.valueField, k);
43921             if(r){
43922                 tv.push(r.data[this.displayField]);
43923             }else if(this.valueNotFoundText !== undefined){
43924                 tv.push( this.valueNotFoundText );
43925             }
43926         },this);
43927        // Roo.log(tv);
43928         
43929         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43930         this.hiddenField.value = v;
43931         this.value = v;
43932     }
43933     
43934 });//<script type="text/javasscript">
43935  
43936
43937 /**
43938  * @class Roo.DDView
43939  * A DnD enabled version of Roo.View.
43940  * @param {Element/String} container The Element in which to create the View.
43941  * @param {String} tpl The template string used to create the markup for each element of the View
43942  * @param {Object} config The configuration properties. These include all the config options of
43943  * {@link Roo.View} plus some specific to this class.<br>
43944  * <p>
43945  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43946  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43947  * <p>
43948  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43949 .x-view-drag-insert-above {
43950         border-top:1px dotted #3366cc;
43951 }
43952 .x-view-drag-insert-below {
43953         border-bottom:1px dotted #3366cc;
43954 }
43955 </code></pre>
43956  * 
43957  */
43958  
43959 Roo.DDView = function(container, tpl, config) {
43960     Roo.DDView.superclass.constructor.apply(this, arguments);
43961     this.getEl().setStyle("outline", "0px none");
43962     this.getEl().unselectable();
43963     if (this.dragGroup) {
43964                 this.setDraggable(this.dragGroup.split(","));
43965     }
43966     if (this.dropGroup) {
43967                 this.setDroppable(this.dropGroup.split(","));
43968     }
43969     if (this.deletable) {
43970         this.setDeletable();
43971     }
43972     this.isDirtyFlag = false;
43973         this.addEvents({
43974                 "drop" : true
43975         });
43976 };
43977
43978 Roo.extend(Roo.DDView, Roo.View, {
43979 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43980 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43981 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43982 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43983
43984         isFormField: true,
43985
43986         reset: Roo.emptyFn,
43987         
43988         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43989
43990         validate: function() {
43991                 return true;
43992         },
43993         
43994         destroy: function() {
43995                 this.purgeListeners();
43996                 this.getEl.removeAllListeners();
43997                 this.getEl().remove();
43998                 if (this.dragZone) {
43999                         if (this.dragZone.destroy) {
44000                                 this.dragZone.destroy();
44001                         }
44002                 }
44003                 if (this.dropZone) {
44004                         if (this.dropZone.destroy) {
44005                                 this.dropZone.destroy();
44006                         }
44007                 }
44008         },
44009
44010 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
44011         getName: function() {
44012                 return this.name;
44013         },
44014
44015 /**     Loads the View from a JSON string representing the Records to put into the Store. */
44016         setValue: function(v) {
44017                 if (!this.store) {
44018                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
44019                 }
44020                 var data = {};
44021                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
44022                 this.store.proxy = new Roo.data.MemoryProxy(data);
44023                 this.store.load();
44024         },
44025
44026 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
44027         getValue: function() {
44028                 var result = '(';
44029                 this.store.each(function(rec) {
44030                         result += rec.id + ',';
44031                 });
44032                 return result.substr(0, result.length - 1) + ')';
44033         },
44034         
44035         getIds: function() {
44036                 var i = 0, result = new Array(this.store.getCount());
44037                 this.store.each(function(rec) {
44038                         result[i++] = rec.id;
44039                 });
44040                 return result;
44041         },
44042         
44043         isDirty: function() {
44044                 return this.isDirtyFlag;
44045         },
44046
44047 /**
44048  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
44049  *      whole Element becomes the target, and this causes the drop gesture to append.
44050  */
44051     getTargetFromEvent : function(e) {
44052                 var target = e.getTarget();
44053                 while ((target !== null) && (target.parentNode != this.el.dom)) {
44054                 target = target.parentNode;
44055                 }
44056                 if (!target) {
44057                         target = this.el.dom.lastChild || this.el.dom;
44058                 }
44059                 return target;
44060     },
44061
44062 /**
44063  *      Create the drag data which consists of an object which has the property "ddel" as
44064  *      the drag proxy element. 
44065  */
44066     getDragData : function(e) {
44067         var target = this.findItemFromChild(e.getTarget());
44068                 if(target) {
44069                         this.handleSelection(e);
44070                         var selNodes = this.getSelectedNodes();
44071             var dragData = {
44072                 source: this,
44073                 copy: this.copy || (this.allowCopy && e.ctrlKey),
44074                 nodes: selNodes,
44075                 records: []
44076                         };
44077                         var selectedIndices = this.getSelectedIndexes();
44078                         for (var i = 0; i < selectedIndices.length; i++) {
44079                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
44080                         }
44081                         if (selNodes.length == 1) {
44082                                 dragData.ddel = target.cloneNode(true); // the div element
44083                         } else {
44084                                 var div = document.createElement('div'); // create the multi element drag "ghost"
44085                                 div.className = 'multi-proxy';
44086                                 for (var i = 0, len = selNodes.length; i < len; i++) {
44087                                         div.appendChild(selNodes[i].cloneNode(true));
44088                                 }
44089                                 dragData.ddel = div;
44090                         }
44091             //console.log(dragData)
44092             //console.log(dragData.ddel.innerHTML)
44093                         return dragData;
44094                 }
44095         //console.log('nodragData')
44096                 return false;
44097     },
44098     
44099 /**     Specify to which ddGroup items in this DDView may be dragged. */
44100     setDraggable: function(ddGroup) {
44101         if (ddGroup instanceof Array) {
44102                 Roo.each(ddGroup, this.setDraggable, this);
44103                 return;
44104         }
44105         if (this.dragZone) {
44106                 this.dragZone.addToGroup(ddGroup);
44107         } else {
44108                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
44109                                 containerScroll: true,
44110                                 ddGroup: ddGroup 
44111
44112                         });
44113 //                      Draggability implies selection. DragZone's mousedown selects the element.
44114                         if (!this.multiSelect) { this.singleSelect = true; }
44115
44116 //                      Wire the DragZone's handlers up to methods in *this*
44117                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
44118                 }
44119     },
44120
44121 /**     Specify from which ddGroup this DDView accepts drops. */
44122     setDroppable: function(ddGroup) {
44123         if (ddGroup instanceof Array) {
44124                 Roo.each(ddGroup, this.setDroppable, this);
44125                 return;
44126         }
44127         if (this.dropZone) {
44128                 this.dropZone.addToGroup(ddGroup);
44129         } else {
44130                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44131                                 containerScroll: true,
44132                                 ddGroup: ddGroup
44133                         });
44134
44135 //                      Wire the DropZone's handlers up to methods in *this*
44136                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44137                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44138                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44139                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44140                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44141                 }
44142     },
44143
44144 /**     Decide whether to drop above or below a View node. */
44145     getDropPoint : function(e, n, dd){
44146         if (n == this.el.dom) { return "above"; }
44147                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44148                 var c = t + (b - t) / 2;
44149                 var y = Roo.lib.Event.getPageY(e);
44150                 if(y <= c) {
44151                         return "above";
44152                 }else{
44153                         return "below";
44154                 }
44155     },
44156
44157     onNodeEnter : function(n, dd, e, data){
44158                 return false;
44159     },
44160     
44161     onNodeOver : function(n, dd, e, data){
44162                 var pt = this.getDropPoint(e, n, dd);
44163                 // set the insert point style on the target node
44164                 var dragElClass = this.dropNotAllowed;
44165                 if (pt) {
44166                         var targetElClass;
44167                         if (pt == "above"){
44168                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44169                                 targetElClass = "x-view-drag-insert-above";
44170                         } else {
44171                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44172                                 targetElClass = "x-view-drag-insert-below";
44173                         }
44174                         if (this.lastInsertClass != targetElClass){
44175                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44176                                 this.lastInsertClass = targetElClass;
44177                         }
44178                 }
44179                 return dragElClass;
44180         },
44181
44182     onNodeOut : function(n, dd, e, data){
44183                 this.removeDropIndicators(n);
44184     },
44185
44186     onNodeDrop : function(n, dd, e, data){
44187         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44188                 return false;
44189         }
44190         var pt = this.getDropPoint(e, n, dd);
44191                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44192                 if (pt == "below") { insertAt++; }
44193                 for (var i = 0; i < data.records.length; i++) {
44194                         var r = data.records[i];
44195                         var dup = this.store.getById(r.id);
44196                         if (dup && (dd != this.dragZone)) {
44197                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44198                         } else {
44199                                 if (data.copy) {
44200                                         this.store.insert(insertAt++, r.copy());
44201                                 } else {
44202                                         data.source.isDirtyFlag = true;
44203                                         r.store.remove(r);
44204                                         this.store.insert(insertAt++, r);
44205                                 }
44206                                 this.isDirtyFlag = true;
44207                         }
44208                 }
44209                 this.dragZone.cachedTarget = null;
44210                 return true;
44211     },
44212
44213     removeDropIndicators : function(n){
44214                 if(n){
44215                         Roo.fly(n).removeClass([
44216                                 "x-view-drag-insert-above",
44217                                 "x-view-drag-insert-below"]);
44218                         this.lastInsertClass = "_noclass";
44219                 }
44220     },
44221
44222 /**
44223  *      Utility method. Add a delete option to the DDView's context menu.
44224  *      @param {String} imageUrl The URL of the "delete" icon image.
44225  */
44226         setDeletable: function(imageUrl) {
44227                 if (!this.singleSelect && !this.multiSelect) {
44228                         this.singleSelect = true;
44229                 }
44230                 var c = this.getContextMenu();
44231                 this.contextMenu.on("itemclick", function(item) {
44232                         switch (item.id) {
44233                                 case "delete":
44234                                         this.remove(this.getSelectedIndexes());
44235                                         break;
44236                         }
44237                 }, this);
44238                 this.contextMenu.add({
44239                         icon: imageUrl,
44240                         id: "delete",
44241                         text: 'Delete'
44242                 });
44243         },
44244         
44245 /**     Return the context menu for this DDView. */
44246         getContextMenu: function() {
44247                 if (!this.contextMenu) {
44248 //                      Create the View's context menu
44249                         this.contextMenu = new Roo.menu.Menu({
44250                                 id: this.id + "-contextmenu"
44251                         });
44252                         this.el.on("contextmenu", this.showContextMenu, this);
44253                 }
44254                 return this.contextMenu;
44255         },
44256         
44257         disableContextMenu: function() {
44258                 if (this.contextMenu) {
44259                         this.el.un("contextmenu", this.showContextMenu, this);
44260                 }
44261         },
44262
44263         showContextMenu: function(e, item) {
44264         item = this.findItemFromChild(e.getTarget());
44265                 if (item) {
44266                         e.stopEvent();
44267                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44268                         this.contextMenu.showAt(e.getXY());
44269             }
44270     },
44271
44272 /**
44273  *      Remove {@link Roo.data.Record}s at the specified indices.
44274  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44275  */
44276     remove: function(selectedIndices) {
44277                 selectedIndices = [].concat(selectedIndices);
44278                 for (var i = 0; i < selectedIndices.length; i++) {
44279                         var rec = this.store.getAt(selectedIndices[i]);
44280                         this.store.remove(rec);
44281                 }
44282     },
44283
44284 /**
44285  *      Double click fires the event, but also, if this is draggable, and there is only one other
44286  *      related DropZone, it transfers the selected node.
44287  */
44288     onDblClick : function(e){
44289         var item = this.findItemFromChild(e.getTarget());
44290         if(item){
44291             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44292                 return false;
44293             }
44294             if (this.dragGroup) {
44295                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44296                     while (targets.indexOf(this.dropZone) > -1) {
44297                             targets.remove(this.dropZone);
44298                                 }
44299                     if (targets.length == 1) {
44300                                         this.dragZone.cachedTarget = null;
44301                         var el = Roo.get(targets[0].getEl());
44302                         var box = el.getBox(true);
44303                         targets[0].onNodeDrop(el.dom, {
44304                                 target: el.dom,
44305                                 xy: [box.x, box.y + box.height - 1]
44306                         }, null, this.getDragData(e));
44307                     }
44308                 }
44309         }
44310     },
44311     
44312     handleSelection: function(e) {
44313                 this.dragZone.cachedTarget = null;
44314         var item = this.findItemFromChild(e.getTarget());
44315         if (!item) {
44316                 this.clearSelections(true);
44317                 return;
44318         }
44319                 if (item && (this.multiSelect || this.singleSelect)){
44320                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44321                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44322                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44323                                 this.unselect(item);
44324                         } else {
44325                                 this.select(item, this.multiSelect && e.ctrlKey);
44326                                 this.lastSelection = item;
44327                         }
44328                 }
44329     },
44330
44331     onItemClick : function(item, index, e){
44332                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44333                         return false;
44334                 }
44335                 return true;
44336     },
44337
44338     unselect : function(nodeInfo, suppressEvent){
44339                 var node = this.getNode(nodeInfo);
44340                 if(node && this.isSelected(node)){
44341                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44342                                 Roo.fly(node).removeClass(this.selectedClass);
44343                                 this.selections.remove(node);
44344                                 if(!suppressEvent){
44345                                         this.fireEvent("selectionchange", this, this.selections);
44346                                 }
44347                         }
44348                 }
44349     }
44350 });
44351 /*
44352  * Based on:
44353  * Ext JS Library 1.1.1
44354  * Copyright(c) 2006-2007, Ext JS, LLC.
44355  *
44356  * Originally Released Under LGPL - original licence link has changed is not relivant.
44357  *
44358  * Fork - LGPL
44359  * <script type="text/javascript">
44360  */
44361  
44362 /**
44363  * @class Roo.LayoutManager
44364  * @extends Roo.util.Observable
44365  * Base class for layout managers.
44366  */
44367 Roo.LayoutManager = function(container, config){
44368     Roo.LayoutManager.superclass.constructor.call(this);
44369     this.el = Roo.get(container);
44370     // ie scrollbar fix
44371     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44372         document.body.scroll = "no";
44373     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44374         this.el.position('relative');
44375     }
44376     this.id = this.el.id;
44377     this.el.addClass("x-layout-container");
44378     /** false to disable window resize monitoring @type Boolean */
44379     this.monitorWindowResize = true;
44380     this.regions = {};
44381     this.addEvents({
44382         /**
44383          * @event layout
44384          * Fires when a layout is performed. 
44385          * @param {Roo.LayoutManager} this
44386          */
44387         "layout" : true,
44388         /**
44389          * @event regionresized
44390          * Fires when the user resizes a region. 
44391          * @param {Roo.LayoutRegion} region The resized region
44392          * @param {Number} newSize The new size (width for east/west, height for north/south)
44393          */
44394         "regionresized" : true,
44395         /**
44396          * @event regioncollapsed
44397          * Fires when a region is collapsed. 
44398          * @param {Roo.LayoutRegion} region The collapsed region
44399          */
44400         "regioncollapsed" : true,
44401         /**
44402          * @event regionexpanded
44403          * Fires when a region is expanded.  
44404          * @param {Roo.LayoutRegion} region The expanded region
44405          */
44406         "regionexpanded" : true
44407     });
44408     this.updating = false;
44409     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44410 };
44411
44412 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44413     /**
44414      * Returns true if this layout is currently being updated
44415      * @return {Boolean}
44416      */
44417     isUpdating : function(){
44418         return this.updating; 
44419     },
44420     
44421     /**
44422      * Suspend the LayoutManager from doing auto-layouts while
44423      * making multiple add or remove calls
44424      */
44425     beginUpdate : function(){
44426         this.updating = true;    
44427     },
44428     
44429     /**
44430      * Restore auto-layouts and optionally disable the manager from performing a layout
44431      * @param {Boolean} noLayout true to disable a layout update 
44432      */
44433     endUpdate : function(noLayout){
44434         this.updating = false;
44435         if(!noLayout){
44436             this.layout();
44437         }    
44438     },
44439     
44440     layout: function(){
44441         
44442     },
44443     
44444     onRegionResized : function(region, newSize){
44445         this.fireEvent("regionresized", region, newSize);
44446         this.layout();
44447     },
44448     
44449     onRegionCollapsed : function(region){
44450         this.fireEvent("regioncollapsed", region);
44451     },
44452     
44453     onRegionExpanded : function(region){
44454         this.fireEvent("regionexpanded", region);
44455     },
44456         
44457     /**
44458      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44459      * performs box-model adjustments.
44460      * @return {Object} The size as an object {width: (the width), height: (the height)}
44461      */
44462     getViewSize : function(){
44463         var size;
44464         if(this.el.dom != document.body){
44465             size = this.el.getSize();
44466         }else{
44467             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44468         }
44469         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44470         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44471         return size;
44472     },
44473     
44474     /**
44475      * Returns the Element this layout is bound to.
44476      * @return {Roo.Element}
44477      */
44478     getEl : function(){
44479         return this.el;
44480     },
44481     
44482     /**
44483      * Returns the specified region.
44484      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44485      * @return {Roo.LayoutRegion}
44486      */
44487     getRegion : function(target){
44488         return this.regions[target.toLowerCase()];
44489     },
44490     
44491     onWindowResize : function(){
44492         if(this.monitorWindowResize){
44493             this.layout();
44494         }
44495     }
44496 });/*
44497  * Based on:
44498  * Ext JS Library 1.1.1
44499  * Copyright(c) 2006-2007, Ext JS, LLC.
44500  *
44501  * Originally Released Under LGPL - original licence link has changed is not relivant.
44502  *
44503  * Fork - LGPL
44504  * <script type="text/javascript">
44505  */
44506 /**
44507  * @class Roo.BorderLayout
44508  * @extends Roo.LayoutManager
44509  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44510  * please see: <br><br>
44511  * <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>
44512  * <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>
44513  * Example:
44514  <pre><code>
44515  var layout = new Roo.BorderLayout(document.body, {
44516     north: {
44517         initialSize: 25,
44518         titlebar: false
44519     },
44520     west: {
44521         split:true,
44522         initialSize: 200,
44523         minSize: 175,
44524         maxSize: 400,
44525         titlebar: true,
44526         collapsible: true
44527     },
44528     east: {
44529         split:true,
44530         initialSize: 202,
44531         minSize: 175,
44532         maxSize: 400,
44533         titlebar: true,
44534         collapsible: true
44535     },
44536     south: {
44537         split:true,
44538         initialSize: 100,
44539         minSize: 100,
44540         maxSize: 200,
44541         titlebar: true,
44542         collapsible: true
44543     },
44544     center: {
44545         titlebar: true,
44546         autoScroll:true,
44547         resizeTabs: true,
44548         minTabWidth: 50,
44549         preferredTabWidth: 150
44550     }
44551 });
44552
44553 // shorthand
44554 var CP = Roo.ContentPanel;
44555
44556 layout.beginUpdate();
44557 layout.add("north", new CP("north", "North"));
44558 layout.add("south", new CP("south", {title: "South", closable: true}));
44559 layout.add("west", new CP("west", {title: "West"}));
44560 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44561 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44562 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44563 layout.getRegion("center").showPanel("center1");
44564 layout.endUpdate();
44565 </code></pre>
44566
44567 <b>The container the layout is rendered into can be either the body element or any other element.
44568 If it is not the body element, the container needs to either be an absolute positioned element,
44569 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44570 the container size if it is not the body element.</b>
44571
44572 * @constructor
44573 * Create a new BorderLayout
44574 * @param {String/HTMLElement/Element} container The container this layout is bound to
44575 * @param {Object} config Configuration options
44576  */
44577 Roo.BorderLayout = function(container, config){
44578     config = config || {};
44579     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44580     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44581     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44582         var target = this.factory.validRegions[i];
44583         if(config[target]){
44584             this.addRegion(target, config[target]);
44585         }
44586     }
44587 };
44588
44589 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44590     /**
44591      * Creates and adds a new region if it doesn't already exist.
44592      * @param {String} target The target region key (north, south, east, west or center).
44593      * @param {Object} config The regions config object
44594      * @return {BorderLayoutRegion} The new region
44595      */
44596     addRegion : function(target, config){
44597         if(!this.regions[target]){
44598             var r = this.factory.create(target, this, config);
44599             this.bindRegion(target, r);
44600         }
44601         return this.regions[target];
44602     },
44603
44604     // private (kinda)
44605     bindRegion : function(name, r){
44606         this.regions[name] = r;
44607         r.on("visibilitychange", this.layout, this);
44608         r.on("paneladded", this.layout, this);
44609         r.on("panelremoved", this.layout, this);
44610         r.on("invalidated", this.layout, this);
44611         r.on("resized", this.onRegionResized, this);
44612         r.on("collapsed", this.onRegionCollapsed, this);
44613         r.on("expanded", this.onRegionExpanded, this);
44614     },
44615
44616     /**
44617      * Performs a layout update.
44618      */
44619     layout : function(){
44620         if(this.updating) return;
44621         var size = this.getViewSize();
44622         var w = size.width;
44623         var h = size.height;
44624         var centerW = w;
44625         var centerH = h;
44626         var centerY = 0;
44627         var centerX = 0;
44628         //var x = 0, y = 0;
44629
44630         var rs = this.regions;
44631         var north = rs["north"];
44632         var south = rs["south"]; 
44633         var west = rs["west"];
44634         var east = rs["east"];
44635         var center = rs["center"];
44636         //if(this.hideOnLayout){ // not supported anymore
44637             //c.el.setStyle("display", "none");
44638         //}
44639         if(north && north.isVisible()){
44640             var b = north.getBox();
44641             var m = north.getMargins();
44642             b.width = w - (m.left+m.right);
44643             b.x = m.left;
44644             b.y = m.top;
44645             centerY = b.height + b.y + m.bottom;
44646             centerH -= centerY;
44647             north.updateBox(this.safeBox(b));
44648         }
44649         if(south && south.isVisible()){
44650             var b = south.getBox();
44651             var m = south.getMargins();
44652             b.width = w - (m.left+m.right);
44653             b.x = m.left;
44654             var totalHeight = (b.height + m.top + m.bottom);
44655             b.y = h - totalHeight + m.top;
44656             centerH -= totalHeight;
44657             south.updateBox(this.safeBox(b));
44658         }
44659         if(west && west.isVisible()){
44660             var b = west.getBox();
44661             var m = west.getMargins();
44662             b.height = centerH - (m.top+m.bottom);
44663             b.x = m.left;
44664             b.y = centerY + m.top;
44665             var totalWidth = (b.width + m.left + m.right);
44666             centerX += totalWidth;
44667             centerW -= totalWidth;
44668             west.updateBox(this.safeBox(b));
44669         }
44670         if(east && east.isVisible()){
44671             var b = east.getBox();
44672             var m = east.getMargins();
44673             b.height = centerH - (m.top+m.bottom);
44674             var totalWidth = (b.width + m.left + m.right);
44675             b.x = w - totalWidth + m.left;
44676             b.y = centerY + m.top;
44677             centerW -= totalWidth;
44678             east.updateBox(this.safeBox(b));
44679         }
44680         if(center){
44681             var m = center.getMargins();
44682             var centerBox = {
44683                 x: centerX + m.left,
44684                 y: centerY + m.top,
44685                 width: centerW - (m.left+m.right),
44686                 height: centerH - (m.top+m.bottom)
44687             };
44688             //if(this.hideOnLayout){
44689                 //center.el.setStyle("display", "block");
44690             //}
44691             center.updateBox(this.safeBox(centerBox));
44692         }
44693         this.el.repaint();
44694         this.fireEvent("layout", this);
44695     },
44696
44697     // private
44698     safeBox : function(box){
44699         box.width = Math.max(0, box.width);
44700         box.height = Math.max(0, box.height);
44701         return box;
44702     },
44703
44704     /**
44705      * Adds a ContentPanel (or subclass) to this layout.
44706      * @param {String} target The target region key (north, south, east, west or center).
44707      * @param {Roo.ContentPanel} panel The panel to add
44708      * @return {Roo.ContentPanel} The added panel
44709      */
44710     add : function(target, panel){
44711          
44712         target = target.toLowerCase();
44713         return this.regions[target].add(panel);
44714     },
44715
44716     /**
44717      * Remove a ContentPanel (or subclass) to this layout.
44718      * @param {String} target The target region key (north, south, east, west or center).
44719      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44720      * @return {Roo.ContentPanel} The removed panel
44721      */
44722     remove : function(target, panel){
44723         target = target.toLowerCase();
44724         return this.regions[target].remove(panel);
44725     },
44726
44727     /**
44728      * Searches all regions for a panel with the specified id
44729      * @param {String} panelId
44730      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44731      */
44732     findPanel : function(panelId){
44733         var rs = this.regions;
44734         for(var target in rs){
44735             if(typeof rs[target] != "function"){
44736                 var p = rs[target].getPanel(panelId);
44737                 if(p){
44738                     return p;
44739                 }
44740             }
44741         }
44742         return null;
44743     },
44744
44745     /**
44746      * Searches all regions for a panel with the specified id and activates (shows) it.
44747      * @param {String/ContentPanel} panelId The panels id or the panel itself
44748      * @return {Roo.ContentPanel} The shown panel or null
44749      */
44750     showPanel : function(panelId) {
44751       var rs = this.regions;
44752       for(var target in rs){
44753          var r = rs[target];
44754          if(typeof r != "function"){
44755             if(r.hasPanel(panelId)){
44756                return r.showPanel(panelId);
44757             }
44758          }
44759       }
44760       return null;
44761    },
44762
44763    /**
44764      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44765      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44766      */
44767     restoreState : function(provider){
44768         if(!provider){
44769             provider = Roo.state.Manager;
44770         }
44771         var sm = new Roo.LayoutStateManager();
44772         sm.init(this, provider);
44773     },
44774
44775     /**
44776      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44777      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44778      * a valid ContentPanel config object.  Example:
44779      * <pre><code>
44780 // Create the main layout
44781 var layout = new Roo.BorderLayout('main-ct', {
44782     west: {
44783         split:true,
44784         minSize: 175,
44785         titlebar: true
44786     },
44787     center: {
44788         title:'Components'
44789     }
44790 }, 'main-ct');
44791
44792 // Create and add multiple ContentPanels at once via configs
44793 layout.batchAdd({
44794    west: {
44795        id: 'source-files',
44796        autoCreate:true,
44797        title:'Ext Source Files',
44798        autoScroll:true,
44799        fitToFrame:true
44800    },
44801    center : {
44802        el: cview,
44803        autoScroll:true,
44804        fitToFrame:true,
44805        toolbar: tb,
44806        resizeEl:'cbody'
44807    }
44808 });
44809 </code></pre>
44810      * @param {Object} regions An object containing ContentPanel configs by region name
44811      */
44812     batchAdd : function(regions){
44813         this.beginUpdate();
44814         for(var rname in regions){
44815             var lr = this.regions[rname];
44816             if(lr){
44817                 this.addTypedPanels(lr, regions[rname]);
44818             }
44819         }
44820         this.endUpdate();
44821     },
44822
44823     // private
44824     addTypedPanels : function(lr, ps){
44825         if(typeof ps == 'string'){
44826             lr.add(new Roo.ContentPanel(ps));
44827         }
44828         else if(ps instanceof Array){
44829             for(var i =0, len = ps.length; i < len; i++){
44830                 this.addTypedPanels(lr, ps[i]);
44831             }
44832         }
44833         else if(!ps.events){ // raw config?
44834             var el = ps.el;
44835             delete ps.el; // prevent conflict
44836             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44837         }
44838         else {  // panel object assumed!
44839             lr.add(ps);
44840         }
44841     },
44842     /**
44843      * Adds a xtype elements to the layout.
44844      * <pre><code>
44845
44846 layout.addxtype({
44847        xtype : 'ContentPanel',
44848        region: 'west',
44849        items: [ .... ]
44850    }
44851 );
44852
44853 layout.addxtype({
44854         xtype : 'NestedLayoutPanel',
44855         region: 'west',
44856         layout: {
44857            center: { },
44858            west: { }   
44859         },
44860         items : [ ... list of content panels or nested layout panels.. ]
44861    }
44862 );
44863 </code></pre>
44864      * @param {Object} cfg Xtype definition of item to add.
44865      */
44866     addxtype : function(cfg)
44867     {
44868         // basically accepts a pannel...
44869         // can accept a layout region..!?!?
44870         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44871         
44872         if (!cfg.xtype.match(/Panel$/)) {
44873             return false;
44874         }
44875         var ret = false;
44876         
44877         if (typeof(cfg.region) == 'undefined') {
44878             Roo.log("Failed to add Panel, region was not set");
44879             Roo.log(cfg);
44880             return false;
44881         }
44882         var region = cfg.region;
44883         delete cfg.region;
44884         
44885           
44886         var xitems = [];
44887         if (cfg.items) {
44888             xitems = cfg.items;
44889             delete cfg.items;
44890         }
44891         var nb = false;
44892         
44893         switch(cfg.xtype) 
44894         {
44895             case 'ContentPanel':  // ContentPanel (el, cfg)
44896             case 'ScrollPanel':  // ContentPanel (el, cfg)
44897                 if(cfg.autoCreate) {
44898                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44899                 } else {
44900                     var el = this.el.createChild();
44901                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44902                 }
44903                 
44904                 this.add(region, ret);
44905                 break;
44906             
44907             
44908             case 'TreePanel': // our new panel!
44909                 cfg.el = this.el.createChild();
44910                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44911                 this.add(region, ret);
44912                 break;
44913             
44914             case 'NestedLayoutPanel': 
44915                 // create a new Layout (which is  a Border Layout...
44916                 var el = this.el.createChild();
44917                 var clayout = cfg.layout;
44918                 delete cfg.layout;
44919                 clayout.items   = clayout.items  || [];
44920                 // replace this exitems with the clayout ones..
44921                 xitems = clayout.items;
44922                  
44923                 
44924                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44925                     cfg.background = false;
44926                 }
44927                 var layout = new Roo.BorderLayout(el, clayout);
44928                 
44929                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44930                 //console.log('adding nested layout panel '  + cfg.toSource());
44931                 this.add(region, ret);
44932                 nb = {}; /// find first...
44933                 break;
44934                 
44935             case 'GridPanel': 
44936             
44937                 // needs grid and region
44938                 
44939                 //var el = this.getRegion(region).el.createChild();
44940                 var el = this.el.createChild();
44941                 // create the grid first...
44942                 
44943                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44944                 delete cfg.grid;
44945                 if (region == 'center' && this.active ) {
44946                     cfg.background = false;
44947                 }
44948                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44949                 
44950                 this.add(region, ret);
44951                 if (cfg.background) {
44952                     ret.on('activate', function(gp) {
44953                         if (!gp.grid.rendered) {
44954                             gp.grid.render();
44955                         }
44956                     });
44957                 } else {
44958                     grid.render();
44959                 }
44960                 break;
44961            
44962                
44963                 
44964                 
44965             default: 
44966                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44967                 return null;
44968              // GridPanel (grid, cfg)
44969             
44970         }
44971         this.beginUpdate();
44972         // add children..
44973         var region = '';
44974         var abn = {};
44975         Roo.each(xitems, function(i)  {
44976             region = nb && i.region ? i.region : false;
44977             
44978             var add = ret.addxtype(i);
44979            
44980             if (region) {
44981                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
44982                 if (!i.background) {
44983                     abn[region] = nb[region] ;
44984                 }
44985             }
44986             
44987         });
44988         this.endUpdate();
44989
44990         // make the last non-background panel active..
44991         //if (nb) { Roo.log(abn); }
44992         if (nb) {
44993             
44994             for(var r in abn) {
44995                 region = this.getRegion(r);
44996                 if (region) {
44997                     // tried using nb[r], but it does not work..
44998                      
44999                     region.showPanel(abn[r]);
45000                    
45001                 }
45002             }
45003         }
45004         return ret;
45005         
45006     }
45007 });
45008
45009 /**
45010  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
45011  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
45012  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
45013  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
45014  * <pre><code>
45015 // shorthand
45016 var CP = Roo.ContentPanel;
45017
45018 var layout = Roo.BorderLayout.create({
45019     north: {
45020         initialSize: 25,
45021         titlebar: false,
45022         panels: [new CP("north", "North")]
45023     },
45024     west: {
45025         split:true,
45026         initialSize: 200,
45027         minSize: 175,
45028         maxSize: 400,
45029         titlebar: true,
45030         collapsible: true,
45031         panels: [new CP("west", {title: "West"})]
45032     },
45033     east: {
45034         split:true,
45035         initialSize: 202,
45036         minSize: 175,
45037         maxSize: 400,
45038         titlebar: true,
45039         collapsible: true,
45040         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
45041     },
45042     south: {
45043         split:true,
45044         initialSize: 100,
45045         minSize: 100,
45046         maxSize: 200,
45047         titlebar: true,
45048         collapsible: true,
45049         panels: [new CP("south", {title: "South", closable: true})]
45050     },
45051     center: {
45052         titlebar: true,
45053         autoScroll:true,
45054         resizeTabs: true,
45055         minTabWidth: 50,
45056         preferredTabWidth: 150,
45057         panels: [
45058             new CP("center1", {title: "Close Me", closable: true}),
45059             new CP("center2", {title: "Center Panel", closable: false})
45060         ]
45061     }
45062 }, document.body);
45063
45064 layout.getRegion("center").showPanel("center1");
45065 </code></pre>
45066  * @param config
45067  * @param targetEl
45068  */
45069 Roo.BorderLayout.create = function(config, targetEl){
45070     var layout = new Roo.BorderLayout(targetEl || document.body, config);
45071     layout.beginUpdate();
45072     var regions = Roo.BorderLayout.RegionFactory.validRegions;
45073     for(var j = 0, jlen = regions.length; j < jlen; j++){
45074         var lr = regions[j];
45075         if(layout.regions[lr] && config[lr].panels){
45076             var r = layout.regions[lr];
45077             var ps = config[lr].panels;
45078             layout.addTypedPanels(r, ps);
45079         }
45080     }
45081     layout.endUpdate();
45082     return layout;
45083 };
45084
45085 // private
45086 Roo.BorderLayout.RegionFactory = {
45087     // private
45088     validRegions : ["north","south","east","west","center"],
45089
45090     // private
45091     create : function(target, mgr, config){
45092         target = target.toLowerCase();
45093         if(config.lightweight || config.basic){
45094             return new Roo.BasicLayoutRegion(mgr, config, target);
45095         }
45096         switch(target){
45097             case "north":
45098                 return new Roo.NorthLayoutRegion(mgr, config);
45099             case "south":
45100                 return new Roo.SouthLayoutRegion(mgr, config);
45101             case "east":
45102                 return new Roo.EastLayoutRegion(mgr, config);
45103             case "west":
45104                 return new Roo.WestLayoutRegion(mgr, config);
45105             case "center":
45106                 return new Roo.CenterLayoutRegion(mgr, config);
45107         }
45108         throw 'Layout region "'+target+'" not supported.';
45109     }
45110 };/*
45111  * Based on:
45112  * Ext JS Library 1.1.1
45113  * Copyright(c) 2006-2007, Ext JS, LLC.
45114  *
45115  * Originally Released Under LGPL - original licence link has changed is not relivant.
45116  *
45117  * Fork - LGPL
45118  * <script type="text/javascript">
45119  */
45120  
45121 /**
45122  * @class Roo.BasicLayoutRegion
45123  * @extends Roo.util.Observable
45124  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
45125  * and does not have a titlebar, tabs or any other features. All it does is size and position 
45126  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
45127  */
45128 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
45129     this.mgr = mgr;
45130     this.position  = pos;
45131     this.events = {
45132         /**
45133          * @scope Roo.BasicLayoutRegion
45134          */
45135         
45136         /**
45137          * @event beforeremove
45138          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45139          * @param {Roo.LayoutRegion} this
45140          * @param {Roo.ContentPanel} panel The panel
45141          * @param {Object} e The cancel event object
45142          */
45143         "beforeremove" : true,
45144         /**
45145          * @event invalidated
45146          * Fires when the layout for this region is changed.
45147          * @param {Roo.LayoutRegion} this
45148          */
45149         "invalidated" : true,
45150         /**
45151          * @event visibilitychange
45152          * Fires when this region is shown or hidden 
45153          * @param {Roo.LayoutRegion} this
45154          * @param {Boolean} visibility true or false
45155          */
45156         "visibilitychange" : true,
45157         /**
45158          * @event paneladded
45159          * Fires when a panel is added. 
45160          * @param {Roo.LayoutRegion} this
45161          * @param {Roo.ContentPanel} panel The panel
45162          */
45163         "paneladded" : true,
45164         /**
45165          * @event panelremoved
45166          * Fires when a panel is removed. 
45167          * @param {Roo.LayoutRegion} this
45168          * @param {Roo.ContentPanel} panel The panel
45169          */
45170         "panelremoved" : true,
45171         /**
45172          * @event collapsed
45173          * Fires when this region is collapsed.
45174          * @param {Roo.LayoutRegion} this
45175          */
45176         "collapsed" : true,
45177         /**
45178          * @event expanded
45179          * Fires when this region is expanded.
45180          * @param {Roo.LayoutRegion} this
45181          */
45182         "expanded" : true,
45183         /**
45184          * @event slideshow
45185          * Fires when this region is slid into view.
45186          * @param {Roo.LayoutRegion} this
45187          */
45188         "slideshow" : true,
45189         /**
45190          * @event slidehide
45191          * Fires when this region slides out of view. 
45192          * @param {Roo.LayoutRegion} this
45193          */
45194         "slidehide" : true,
45195         /**
45196          * @event panelactivated
45197          * Fires when a panel is activated. 
45198          * @param {Roo.LayoutRegion} this
45199          * @param {Roo.ContentPanel} panel The activated panel
45200          */
45201         "panelactivated" : true,
45202         /**
45203          * @event resized
45204          * Fires when the user resizes this region. 
45205          * @param {Roo.LayoutRegion} this
45206          * @param {Number} newSize The new size (width for east/west, height for north/south)
45207          */
45208         "resized" : true
45209     };
45210     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45211     this.panels = new Roo.util.MixedCollection();
45212     this.panels.getKey = this.getPanelId.createDelegate(this);
45213     this.box = null;
45214     this.activePanel = null;
45215     // ensure listeners are added...
45216     
45217     if (config.listeners || config.events) {
45218         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45219             listeners : config.listeners || {},
45220             events : config.events || {}
45221         });
45222     }
45223     
45224     if(skipConfig !== true){
45225         this.applyConfig(config);
45226     }
45227 };
45228
45229 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45230     getPanelId : function(p){
45231         return p.getId();
45232     },
45233     
45234     applyConfig : function(config){
45235         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45236         this.config = config;
45237         
45238     },
45239     
45240     /**
45241      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45242      * the width, for horizontal (north, south) the height.
45243      * @param {Number} newSize The new width or height
45244      */
45245     resizeTo : function(newSize){
45246         var el = this.el ? this.el :
45247                  (this.activePanel ? this.activePanel.getEl() : null);
45248         if(el){
45249             switch(this.position){
45250                 case "east":
45251                 case "west":
45252                     el.setWidth(newSize);
45253                     this.fireEvent("resized", this, newSize);
45254                 break;
45255                 case "north":
45256                 case "south":
45257                     el.setHeight(newSize);
45258                     this.fireEvent("resized", this, newSize);
45259                 break;                
45260             }
45261         }
45262     },
45263     
45264     getBox : function(){
45265         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45266     },
45267     
45268     getMargins : function(){
45269         return this.margins;
45270     },
45271     
45272     updateBox : function(box){
45273         this.box = box;
45274         var el = this.activePanel.getEl();
45275         el.dom.style.left = box.x + "px";
45276         el.dom.style.top = box.y + "px";
45277         this.activePanel.setSize(box.width, box.height);
45278     },
45279     
45280     /**
45281      * Returns the container element for this region.
45282      * @return {Roo.Element}
45283      */
45284     getEl : function(){
45285         return this.activePanel;
45286     },
45287     
45288     /**
45289      * Returns true if this region is currently visible.
45290      * @return {Boolean}
45291      */
45292     isVisible : function(){
45293         return this.activePanel ? true : false;
45294     },
45295     
45296     setActivePanel : function(panel){
45297         panel = this.getPanel(panel);
45298         if(this.activePanel && this.activePanel != panel){
45299             this.activePanel.setActiveState(false);
45300             this.activePanel.getEl().setLeftTop(-10000,-10000);
45301         }
45302         this.activePanel = panel;
45303         panel.setActiveState(true);
45304         if(this.box){
45305             panel.setSize(this.box.width, this.box.height);
45306         }
45307         this.fireEvent("panelactivated", this, panel);
45308         this.fireEvent("invalidated");
45309     },
45310     
45311     /**
45312      * Show the specified panel.
45313      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45314      * @return {Roo.ContentPanel} The shown panel or null
45315      */
45316     showPanel : function(panel){
45317         if(panel = this.getPanel(panel)){
45318             this.setActivePanel(panel);
45319         }
45320         return panel;
45321     },
45322     
45323     /**
45324      * Get the active panel for this region.
45325      * @return {Roo.ContentPanel} The active panel or null
45326      */
45327     getActivePanel : function(){
45328         return this.activePanel;
45329     },
45330     
45331     /**
45332      * Add the passed ContentPanel(s)
45333      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45334      * @return {Roo.ContentPanel} The panel added (if only one was added)
45335      */
45336     add : function(panel){
45337         if(arguments.length > 1){
45338             for(var i = 0, len = arguments.length; i < len; i++) {
45339                 this.add(arguments[i]);
45340             }
45341             return null;
45342         }
45343         if(this.hasPanel(panel)){
45344             this.showPanel(panel);
45345             return panel;
45346         }
45347         var el = panel.getEl();
45348         if(el.dom.parentNode != this.mgr.el.dom){
45349             this.mgr.el.dom.appendChild(el.dom);
45350         }
45351         if(panel.setRegion){
45352             panel.setRegion(this);
45353         }
45354         this.panels.add(panel);
45355         el.setStyle("position", "absolute");
45356         if(!panel.background){
45357             this.setActivePanel(panel);
45358             if(this.config.initialSize && this.panels.getCount()==1){
45359                 this.resizeTo(this.config.initialSize);
45360             }
45361         }
45362         this.fireEvent("paneladded", this, panel);
45363         return panel;
45364     },
45365     
45366     /**
45367      * Returns true if the panel is in this region.
45368      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45369      * @return {Boolean}
45370      */
45371     hasPanel : function(panel){
45372         if(typeof panel == "object"){ // must be panel obj
45373             panel = panel.getId();
45374         }
45375         return this.getPanel(panel) ? true : false;
45376     },
45377     
45378     /**
45379      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45380      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45381      * @param {Boolean} preservePanel Overrides the config preservePanel option
45382      * @return {Roo.ContentPanel} The panel that was removed
45383      */
45384     remove : function(panel, preservePanel){
45385         panel = this.getPanel(panel);
45386         if(!panel){
45387             return null;
45388         }
45389         var e = {};
45390         this.fireEvent("beforeremove", this, panel, e);
45391         if(e.cancel === true){
45392             return null;
45393         }
45394         var panelId = panel.getId();
45395         this.panels.removeKey(panelId);
45396         return panel;
45397     },
45398     
45399     /**
45400      * Returns the panel specified or null if it's not in this region.
45401      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45402      * @return {Roo.ContentPanel}
45403      */
45404     getPanel : function(id){
45405         if(typeof id == "object"){ // must be panel obj
45406             return id;
45407         }
45408         return this.panels.get(id);
45409     },
45410     
45411     /**
45412      * Returns this regions position (north/south/east/west/center).
45413      * @return {String} 
45414      */
45415     getPosition: function(){
45416         return this.position;    
45417     }
45418 });/*
45419  * Based on:
45420  * Ext JS Library 1.1.1
45421  * Copyright(c) 2006-2007, Ext JS, LLC.
45422  *
45423  * Originally Released Under LGPL - original licence link has changed is not relivant.
45424  *
45425  * Fork - LGPL
45426  * <script type="text/javascript">
45427  */
45428  
45429 /**
45430  * @class Roo.LayoutRegion
45431  * @extends Roo.BasicLayoutRegion
45432  * This class represents a region in a layout manager.
45433  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45434  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45435  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45436  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45437  * @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})
45438  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45439  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45440  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45441  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45442  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45443  * @cfg {String}    title           The title for the region (overrides panel titles)
45444  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45445  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45446  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45447  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45448  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45449  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45450  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45451  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45452  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45453  * @cfg {Boolean}   showPin         True to show a pin button
45454  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45455  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45456  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45457  * @cfg {Number}    width           For East/West panels
45458  * @cfg {Number}    height          For North/South panels
45459  * @cfg {Boolean}   split           To show the splitter
45460  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45461  */
45462 Roo.LayoutRegion = function(mgr, config, pos){
45463     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45464     var dh = Roo.DomHelper;
45465     /** This region's container element 
45466     * @type Roo.Element */
45467     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45468     /** This region's title element 
45469     * @type Roo.Element */
45470
45471     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45472         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45473         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45474     ]}, true);
45475     this.titleEl.enableDisplayMode();
45476     /** This region's title text element 
45477     * @type HTMLElement */
45478     this.titleTextEl = this.titleEl.dom.firstChild;
45479     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45480     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45481     this.closeBtn.enableDisplayMode();
45482     this.closeBtn.on("click", this.closeClicked, this);
45483     this.closeBtn.hide();
45484
45485     this.createBody(config);
45486     this.visible = true;
45487     this.collapsed = false;
45488
45489     if(config.hideWhenEmpty){
45490         this.hide();
45491         this.on("paneladded", this.validateVisibility, this);
45492         this.on("panelremoved", this.validateVisibility, this);
45493     }
45494     this.applyConfig(config);
45495 };
45496
45497 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45498
45499     createBody : function(){
45500         /** This region's body element 
45501         * @type Roo.Element */
45502         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45503     },
45504
45505     applyConfig : function(c){
45506         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45507             var dh = Roo.DomHelper;
45508             if(c.titlebar !== false){
45509                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45510                 this.collapseBtn.on("click", this.collapse, this);
45511                 this.collapseBtn.enableDisplayMode();
45512
45513                 if(c.showPin === true || this.showPin){
45514                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45515                     this.stickBtn.enableDisplayMode();
45516                     this.stickBtn.on("click", this.expand, this);
45517                     this.stickBtn.hide();
45518                 }
45519             }
45520             /** This region's collapsed element
45521             * @type Roo.Element */
45522             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45523                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45524             ]}, true);
45525             if(c.floatable !== false){
45526                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45527                this.collapsedEl.on("click", this.collapseClick, this);
45528             }
45529
45530             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45531                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45532                    id: "message", unselectable: "on", style:{"float":"left"}});
45533                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45534              }
45535             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45536             this.expandBtn.on("click", this.expand, this);
45537         }
45538         if(this.collapseBtn){
45539             this.collapseBtn.setVisible(c.collapsible == true);
45540         }
45541         this.cmargins = c.cmargins || this.cmargins ||
45542                          (this.position == "west" || this.position == "east" ?
45543                              {top: 0, left: 2, right:2, bottom: 0} :
45544                              {top: 2, left: 0, right:0, bottom: 2});
45545         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45546         this.bottomTabs = c.tabPosition != "top";
45547         this.autoScroll = c.autoScroll || false;
45548         if(this.autoScroll){
45549             this.bodyEl.setStyle("overflow", "auto");
45550         }else{
45551             this.bodyEl.setStyle("overflow", "hidden");
45552         }
45553         //if(c.titlebar !== false){
45554             if((!c.titlebar && !c.title) || c.titlebar === false){
45555                 this.titleEl.hide();
45556             }else{
45557                 this.titleEl.show();
45558                 if(c.title){
45559                     this.titleTextEl.innerHTML = c.title;
45560                 }
45561             }
45562         //}
45563         this.duration = c.duration || .30;
45564         this.slideDuration = c.slideDuration || .45;
45565         this.config = c;
45566         if(c.collapsed){
45567             this.collapse(true);
45568         }
45569         if(c.hidden){
45570             this.hide();
45571         }
45572     },
45573     /**
45574      * Returns true if this region is currently visible.
45575      * @return {Boolean}
45576      */
45577     isVisible : function(){
45578         return this.visible;
45579     },
45580
45581     /**
45582      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45583      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45584      */
45585     setCollapsedTitle : function(title){
45586         title = title || "&#160;";
45587         if(this.collapsedTitleTextEl){
45588             this.collapsedTitleTextEl.innerHTML = title;
45589         }
45590     },
45591
45592     getBox : function(){
45593         var b;
45594         if(!this.collapsed){
45595             b = this.el.getBox(false, true);
45596         }else{
45597             b = this.collapsedEl.getBox(false, true);
45598         }
45599         return b;
45600     },
45601
45602     getMargins : function(){
45603         return this.collapsed ? this.cmargins : this.margins;
45604     },
45605
45606     highlight : function(){
45607         this.el.addClass("x-layout-panel-dragover");
45608     },
45609
45610     unhighlight : function(){
45611         this.el.removeClass("x-layout-panel-dragover");
45612     },
45613
45614     updateBox : function(box){
45615         this.box = box;
45616         if(!this.collapsed){
45617             this.el.dom.style.left = box.x + "px";
45618             this.el.dom.style.top = box.y + "px";
45619             this.updateBody(box.width, box.height);
45620         }else{
45621             this.collapsedEl.dom.style.left = box.x + "px";
45622             this.collapsedEl.dom.style.top = box.y + "px";
45623             this.collapsedEl.setSize(box.width, box.height);
45624         }
45625         if(this.tabs){
45626             this.tabs.autoSizeTabs();
45627         }
45628     },
45629
45630     updateBody : function(w, h){
45631         if(w !== null){
45632             this.el.setWidth(w);
45633             w -= this.el.getBorderWidth("rl");
45634             if(this.config.adjustments){
45635                 w += this.config.adjustments[0];
45636             }
45637         }
45638         if(h !== null){
45639             this.el.setHeight(h);
45640             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45641             h -= this.el.getBorderWidth("tb");
45642             if(this.config.adjustments){
45643                 h += this.config.adjustments[1];
45644             }
45645             this.bodyEl.setHeight(h);
45646             if(this.tabs){
45647                 h = this.tabs.syncHeight(h);
45648             }
45649         }
45650         if(this.panelSize){
45651             w = w !== null ? w : this.panelSize.width;
45652             h = h !== null ? h : this.panelSize.height;
45653         }
45654         if(this.activePanel){
45655             var el = this.activePanel.getEl();
45656             w = w !== null ? w : el.getWidth();
45657             h = h !== null ? h : el.getHeight();
45658             this.panelSize = {width: w, height: h};
45659             this.activePanel.setSize(w, h);
45660         }
45661         if(Roo.isIE && this.tabs){
45662             this.tabs.el.repaint();
45663         }
45664     },
45665
45666     /**
45667      * Returns the container element for this region.
45668      * @return {Roo.Element}
45669      */
45670     getEl : function(){
45671         return this.el;
45672     },
45673
45674     /**
45675      * Hides this region.
45676      */
45677     hide : function(){
45678         if(!this.collapsed){
45679             this.el.dom.style.left = "-2000px";
45680             this.el.hide();
45681         }else{
45682             this.collapsedEl.dom.style.left = "-2000px";
45683             this.collapsedEl.hide();
45684         }
45685         this.visible = false;
45686         this.fireEvent("visibilitychange", this, false);
45687     },
45688
45689     /**
45690      * Shows this region if it was previously hidden.
45691      */
45692     show : function(){
45693         if(!this.collapsed){
45694             this.el.show();
45695         }else{
45696             this.collapsedEl.show();
45697         }
45698         this.visible = true;
45699         this.fireEvent("visibilitychange", this, true);
45700     },
45701
45702     closeClicked : function(){
45703         if(this.activePanel){
45704             this.remove(this.activePanel);
45705         }
45706     },
45707
45708     collapseClick : function(e){
45709         if(this.isSlid){
45710            e.stopPropagation();
45711            this.slideIn();
45712         }else{
45713            e.stopPropagation();
45714            this.slideOut();
45715         }
45716     },
45717
45718     /**
45719      * Collapses this region.
45720      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45721      */
45722     collapse : function(skipAnim){
45723         if(this.collapsed) return;
45724         this.collapsed = true;
45725         if(this.split){
45726             this.split.el.hide();
45727         }
45728         if(this.config.animate && skipAnim !== true){
45729             this.fireEvent("invalidated", this);
45730             this.animateCollapse();
45731         }else{
45732             this.el.setLocation(-20000,-20000);
45733             this.el.hide();
45734             this.collapsedEl.show();
45735             this.fireEvent("collapsed", this);
45736             this.fireEvent("invalidated", this);
45737         }
45738     },
45739
45740     animateCollapse : function(){
45741         // overridden
45742     },
45743
45744     /**
45745      * Expands this region if it was previously collapsed.
45746      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45747      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45748      */
45749     expand : function(e, skipAnim){
45750         if(e) e.stopPropagation();
45751         if(!this.collapsed || this.el.hasActiveFx()) return;
45752         if(this.isSlid){
45753             this.afterSlideIn();
45754             skipAnim = true;
45755         }
45756         this.collapsed = false;
45757         if(this.config.animate && skipAnim !== true){
45758             this.animateExpand();
45759         }else{
45760             this.el.show();
45761             if(this.split){
45762                 this.split.el.show();
45763             }
45764             this.collapsedEl.setLocation(-2000,-2000);
45765             this.collapsedEl.hide();
45766             this.fireEvent("invalidated", this);
45767             this.fireEvent("expanded", this);
45768         }
45769     },
45770
45771     animateExpand : function(){
45772         // overridden
45773     },
45774
45775     initTabs : function()
45776     {
45777         this.bodyEl.setStyle("overflow", "hidden");
45778         var ts = new Roo.TabPanel(
45779                 this.bodyEl.dom,
45780                 {
45781                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45782                     disableTooltips: this.config.disableTabTips,
45783                     toolbar : this.config.toolbar
45784                 }
45785         );
45786         if(this.config.hideTabs){
45787             ts.stripWrap.setDisplayed(false);
45788         }
45789         this.tabs = ts;
45790         ts.resizeTabs = this.config.resizeTabs === true;
45791         ts.minTabWidth = this.config.minTabWidth || 40;
45792         ts.maxTabWidth = this.config.maxTabWidth || 250;
45793         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45794         ts.monitorResize = false;
45795         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45796         ts.bodyEl.addClass('x-layout-tabs-body');
45797         this.panels.each(this.initPanelAsTab, this);
45798     },
45799
45800     initPanelAsTab : function(panel){
45801         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45802                     this.config.closeOnTab && panel.isClosable());
45803         if(panel.tabTip !== undefined){
45804             ti.setTooltip(panel.tabTip);
45805         }
45806         ti.on("activate", function(){
45807               this.setActivePanel(panel);
45808         }, this);
45809         if(this.config.closeOnTab){
45810             ti.on("beforeclose", function(t, e){
45811                 e.cancel = true;
45812                 this.remove(panel);
45813             }, this);
45814         }
45815         return ti;
45816     },
45817
45818     updatePanelTitle : function(panel, title){
45819         if(this.activePanel == panel){
45820             this.updateTitle(title);
45821         }
45822         if(this.tabs){
45823             var ti = this.tabs.getTab(panel.getEl().id);
45824             ti.setText(title);
45825             if(panel.tabTip !== undefined){
45826                 ti.setTooltip(panel.tabTip);
45827             }
45828         }
45829     },
45830
45831     updateTitle : function(title){
45832         if(this.titleTextEl && !this.config.title){
45833             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45834         }
45835     },
45836
45837     setActivePanel : function(panel){
45838         panel = this.getPanel(panel);
45839         if(this.activePanel && this.activePanel != panel){
45840             this.activePanel.setActiveState(false);
45841         }
45842         this.activePanel = panel;
45843         panel.setActiveState(true);
45844         if(this.panelSize){
45845             panel.setSize(this.panelSize.width, this.panelSize.height);
45846         }
45847         if(this.closeBtn){
45848             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45849         }
45850         this.updateTitle(panel.getTitle());
45851         if(this.tabs){
45852             this.fireEvent("invalidated", this);
45853         }
45854         this.fireEvent("panelactivated", this, panel);
45855     },
45856
45857     /**
45858      * Shows the specified panel.
45859      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45860      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45861      */
45862     showPanel : function(panel){
45863         if(panel = this.getPanel(panel)){
45864             if(this.tabs){
45865                 var tab = this.tabs.getTab(panel.getEl().id);
45866                 if(tab.isHidden()){
45867                     this.tabs.unhideTab(tab.id);
45868                 }
45869                 tab.activate();
45870             }else{
45871                 this.setActivePanel(panel);
45872             }
45873         }
45874         return panel;
45875     },
45876
45877     /**
45878      * Get the active panel for this region.
45879      * @return {Roo.ContentPanel} The active panel or null
45880      */
45881     getActivePanel : function(){
45882         return this.activePanel;
45883     },
45884
45885     validateVisibility : function(){
45886         if(this.panels.getCount() < 1){
45887             this.updateTitle("&#160;");
45888             this.closeBtn.hide();
45889             this.hide();
45890         }else{
45891             if(!this.isVisible()){
45892                 this.show();
45893             }
45894         }
45895     },
45896
45897     /**
45898      * Adds the passed ContentPanel(s) to this region.
45899      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45900      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45901      */
45902     add : function(panel){
45903         if(arguments.length > 1){
45904             for(var i = 0, len = arguments.length; i < len; i++) {
45905                 this.add(arguments[i]);
45906             }
45907             return null;
45908         }
45909         if(this.hasPanel(panel)){
45910             this.showPanel(panel);
45911             return panel;
45912         }
45913         panel.setRegion(this);
45914         this.panels.add(panel);
45915         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45916             this.bodyEl.dom.appendChild(panel.getEl().dom);
45917             if(panel.background !== true){
45918                 this.setActivePanel(panel);
45919             }
45920             this.fireEvent("paneladded", this, panel);
45921             return panel;
45922         }
45923         if(!this.tabs){
45924             this.initTabs();
45925         }else{
45926             this.initPanelAsTab(panel);
45927         }
45928         if(panel.background !== true){
45929             this.tabs.activate(panel.getEl().id);
45930         }
45931         this.fireEvent("paneladded", this, panel);
45932         return panel;
45933     },
45934
45935     /**
45936      * Hides the tab for the specified panel.
45937      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45938      */
45939     hidePanel : function(panel){
45940         if(this.tabs && (panel = this.getPanel(panel))){
45941             this.tabs.hideTab(panel.getEl().id);
45942         }
45943     },
45944
45945     /**
45946      * Unhides the tab for a previously hidden panel.
45947      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45948      */
45949     unhidePanel : function(panel){
45950         if(this.tabs && (panel = this.getPanel(panel))){
45951             this.tabs.unhideTab(panel.getEl().id);
45952         }
45953     },
45954
45955     clearPanels : function(){
45956         while(this.panels.getCount() > 0){
45957              this.remove(this.panels.first());
45958         }
45959     },
45960
45961     /**
45962      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45963      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45964      * @param {Boolean} preservePanel Overrides the config preservePanel option
45965      * @return {Roo.ContentPanel} The panel that was removed
45966      */
45967     remove : function(panel, preservePanel){
45968         panel = this.getPanel(panel);
45969         if(!panel){
45970             return null;
45971         }
45972         var e = {};
45973         this.fireEvent("beforeremove", this, panel, e);
45974         if(e.cancel === true){
45975             return null;
45976         }
45977         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45978         var panelId = panel.getId();
45979         this.panels.removeKey(panelId);
45980         if(preservePanel){
45981             document.body.appendChild(panel.getEl().dom);
45982         }
45983         if(this.tabs){
45984             this.tabs.removeTab(panel.getEl().id);
45985         }else if (!preservePanel){
45986             this.bodyEl.dom.removeChild(panel.getEl().dom);
45987         }
45988         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45989             var p = this.panels.first();
45990             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45991             tempEl.appendChild(p.getEl().dom);
45992             this.bodyEl.update("");
45993             this.bodyEl.dom.appendChild(p.getEl().dom);
45994             tempEl = null;
45995             this.updateTitle(p.getTitle());
45996             this.tabs = null;
45997             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45998             this.setActivePanel(p);
45999         }
46000         panel.setRegion(null);
46001         if(this.activePanel == panel){
46002             this.activePanel = null;
46003         }
46004         if(this.config.autoDestroy !== false && preservePanel !== true){
46005             try{panel.destroy();}catch(e){}
46006         }
46007         this.fireEvent("panelremoved", this, panel);
46008         return panel;
46009     },
46010
46011     /**
46012      * Returns the TabPanel component used by this region
46013      * @return {Roo.TabPanel}
46014      */
46015     getTabs : function(){
46016         return this.tabs;
46017     },
46018
46019     createTool : function(parentEl, className){
46020         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
46021             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
46022         btn.addClassOnOver("x-layout-tools-button-over");
46023         return btn;
46024     }
46025 });/*
46026  * Based on:
46027  * Ext JS Library 1.1.1
46028  * Copyright(c) 2006-2007, Ext JS, LLC.
46029  *
46030  * Originally Released Under LGPL - original licence link has changed is not relivant.
46031  *
46032  * Fork - LGPL
46033  * <script type="text/javascript">
46034  */
46035  
46036
46037
46038 /**
46039  * @class Roo.SplitLayoutRegion
46040  * @extends Roo.LayoutRegion
46041  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
46042  */
46043 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
46044     this.cursor = cursor;
46045     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
46046 };
46047
46048 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
46049     splitTip : "Drag to resize.",
46050     collapsibleSplitTip : "Drag to resize. Double click to hide.",
46051     useSplitTips : false,
46052
46053     applyConfig : function(config){
46054         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
46055         if(config.split){
46056             if(!this.split){
46057                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
46058                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
46059                 /** The SplitBar for this region 
46060                 * @type Roo.SplitBar */
46061                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
46062                 this.split.on("moved", this.onSplitMove, this);
46063                 this.split.useShim = config.useShim === true;
46064                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
46065                 if(this.useSplitTips){
46066                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
46067                 }
46068                 if(config.collapsible){
46069                     this.split.el.on("dblclick", this.collapse,  this);
46070                 }
46071             }
46072             if(typeof config.minSize != "undefined"){
46073                 this.split.minSize = config.minSize;
46074             }
46075             if(typeof config.maxSize != "undefined"){
46076                 this.split.maxSize = config.maxSize;
46077             }
46078             if(config.hideWhenEmpty || config.hidden || config.collapsed){
46079                 this.hideSplitter();
46080             }
46081         }
46082     },
46083
46084     getHMaxSize : function(){
46085          var cmax = this.config.maxSize || 10000;
46086          var center = this.mgr.getRegion("center");
46087          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
46088     },
46089
46090     getVMaxSize : function(){
46091          var cmax = this.config.maxSize || 10000;
46092          var center = this.mgr.getRegion("center");
46093          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
46094     },
46095
46096     onSplitMove : function(split, newSize){
46097         this.fireEvent("resized", this, newSize);
46098     },
46099     
46100     /** 
46101      * Returns the {@link Roo.SplitBar} for this region.
46102      * @return {Roo.SplitBar}
46103      */
46104     getSplitBar : function(){
46105         return this.split;
46106     },
46107     
46108     hide : function(){
46109         this.hideSplitter();
46110         Roo.SplitLayoutRegion.superclass.hide.call(this);
46111     },
46112
46113     hideSplitter : function(){
46114         if(this.split){
46115             this.split.el.setLocation(-2000,-2000);
46116             this.split.el.hide();
46117         }
46118     },
46119
46120     show : function(){
46121         if(this.split){
46122             this.split.el.show();
46123         }
46124         Roo.SplitLayoutRegion.superclass.show.call(this);
46125     },
46126     
46127     beforeSlide: function(){
46128         if(Roo.isGecko){// firefox overflow auto bug workaround
46129             this.bodyEl.clip();
46130             if(this.tabs) this.tabs.bodyEl.clip();
46131             if(this.activePanel){
46132                 this.activePanel.getEl().clip();
46133                 
46134                 if(this.activePanel.beforeSlide){
46135                     this.activePanel.beforeSlide();
46136                 }
46137             }
46138         }
46139     },
46140     
46141     afterSlide : function(){
46142         if(Roo.isGecko){// firefox overflow auto bug workaround
46143             this.bodyEl.unclip();
46144             if(this.tabs) this.tabs.bodyEl.unclip();
46145             if(this.activePanel){
46146                 this.activePanel.getEl().unclip();
46147                 if(this.activePanel.afterSlide){
46148                     this.activePanel.afterSlide();
46149                 }
46150             }
46151         }
46152     },
46153
46154     initAutoHide : function(){
46155         if(this.autoHide !== false){
46156             if(!this.autoHideHd){
46157                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46158                 this.autoHideHd = {
46159                     "mouseout": function(e){
46160                         if(!e.within(this.el, true)){
46161                             st.delay(500);
46162                         }
46163                     },
46164                     "mouseover" : function(e){
46165                         st.cancel();
46166                     },
46167                     scope : this
46168                 };
46169             }
46170             this.el.on(this.autoHideHd);
46171         }
46172     },
46173
46174     clearAutoHide : function(){
46175         if(this.autoHide !== false){
46176             this.el.un("mouseout", this.autoHideHd.mouseout);
46177             this.el.un("mouseover", this.autoHideHd.mouseover);
46178         }
46179     },
46180
46181     clearMonitor : function(){
46182         Roo.get(document).un("click", this.slideInIf, this);
46183     },
46184
46185     // these names are backwards but not changed for compat
46186     slideOut : function(){
46187         if(this.isSlid || this.el.hasActiveFx()){
46188             return;
46189         }
46190         this.isSlid = true;
46191         if(this.collapseBtn){
46192             this.collapseBtn.hide();
46193         }
46194         this.closeBtnState = this.closeBtn.getStyle('display');
46195         this.closeBtn.hide();
46196         if(this.stickBtn){
46197             this.stickBtn.show();
46198         }
46199         this.el.show();
46200         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46201         this.beforeSlide();
46202         this.el.setStyle("z-index", 10001);
46203         this.el.slideIn(this.getSlideAnchor(), {
46204             callback: function(){
46205                 this.afterSlide();
46206                 this.initAutoHide();
46207                 Roo.get(document).on("click", this.slideInIf, this);
46208                 this.fireEvent("slideshow", this);
46209             },
46210             scope: this,
46211             block: true
46212         });
46213     },
46214
46215     afterSlideIn : function(){
46216         this.clearAutoHide();
46217         this.isSlid = false;
46218         this.clearMonitor();
46219         this.el.setStyle("z-index", "");
46220         if(this.collapseBtn){
46221             this.collapseBtn.show();
46222         }
46223         this.closeBtn.setStyle('display', this.closeBtnState);
46224         if(this.stickBtn){
46225             this.stickBtn.hide();
46226         }
46227         this.fireEvent("slidehide", this);
46228     },
46229
46230     slideIn : function(cb){
46231         if(!this.isSlid || this.el.hasActiveFx()){
46232             Roo.callback(cb);
46233             return;
46234         }
46235         this.isSlid = false;
46236         this.beforeSlide();
46237         this.el.slideOut(this.getSlideAnchor(), {
46238             callback: function(){
46239                 this.el.setLeftTop(-10000, -10000);
46240                 this.afterSlide();
46241                 this.afterSlideIn();
46242                 Roo.callback(cb);
46243             },
46244             scope: this,
46245             block: true
46246         });
46247     },
46248     
46249     slideInIf : function(e){
46250         if(!e.within(this.el)){
46251             this.slideIn();
46252         }
46253     },
46254
46255     animateCollapse : function(){
46256         this.beforeSlide();
46257         this.el.setStyle("z-index", 20000);
46258         var anchor = this.getSlideAnchor();
46259         this.el.slideOut(anchor, {
46260             callback : function(){
46261                 this.el.setStyle("z-index", "");
46262                 this.collapsedEl.slideIn(anchor, {duration:.3});
46263                 this.afterSlide();
46264                 this.el.setLocation(-10000,-10000);
46265                 this.el.hide();
46266                 this.fireEvent("collapsed", this);
46267             },
46268             scope: this,
46269             block: true
46270         });
46271     },
46272
46273     animateExpand : function(){
46274         this.beforeSlide();
46275         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46276         this.el.setStyle("z-index", 20000);
46277         this.collapsedEl.hide({
46278             duration:.1
46279         });
46280         this.el.slideIn(this.getSlideAnchor(), {
46281             callback : function(){
46282                 this.el.setStyle("z-index", "");
46283                 this.afterSlide();
46284                 if(this.split){
46285                     this.split.el.show();
46286                 }
46287                 this.fireEvent("invalidated", this);
46288                 this.fireEvent("expanded", this);
46289             },
46290             scope: this,
46291             block: true
46292         });
46293     },
46294
46295     anchors : {
46296         "west" : "left",
46297         "east" : "right",
46298         "north" : "top",
46299         "south" : "bottom"
46300     },
46301
46302     sanchors : {
46303         "west" : "l",
46304         "east" : "r",
46305         "north" : "t",
46306         "south" : "b"
46307     },
46308
46309     canchors : {
46310         "west" : "tl-tr",
46311         "east" : "tr-tl",
46312         "north" : "tl-bl",
46313         "south" : "bl-tl"
46314     },
46315
46316     getAnchor : function(){
46317         return this.anchors[this.position];
46318     },
46319
46320     getCollapseAnchor : function(){
46321         return this.canchors[this.position];
46322     },
46323
46324     getSlideAnchor : function(){
46325         return this.sanchors[this.position];
46326     },
46327
46328     getAlignAdj : function(){
46329         var cm = this.cmargins;
46330         switch(this.position){
46331             case "west":
46332                 return [0, 0];
46333             break;
46334             case "east":
46335                 return [0, 0];
46336             break;
46337             case "north":
46338                 return [0, 0];
46339             break;
46340             case "south":
46341                 return [0, 0];
46342             break;
46343         }
46344     },
46345
46346     getExpandAdj : function(){
46347         var c = this.collapsedEl, cm = this.cmargins;
46348         switch(this.position){
46349             case "west":
46350                 return [-(cm.right+c.getWidth()+cm.left), 0];
46351             break;
46352             case "east":
46353                 return [cm.right+c.getWidth()+cm.left, 0];
46354             break;
46355             case "north":
46356                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46357             break;
46358             case "south":
46359                 return [0, cm.top+cm.bottom+c.getHeight()];
46360             break;
46361         }
46362     }
46363 });/*
46364  * Based on:
46365  * Ext JS Library 1.1.1
46366  * Copyright(c) 2006-2007, Ext JS, LLC.
46367  *
46368  * Originally Released Under LGPL - original licence link has changed is not relivant.
46369  *
46370  * Fork - LGPL
46371  * <script type="text/javascript">
46372  */
46373 /*
46374  * These classes are private internal classes
46375  */
46376 Roo.CenterLayoutRegion = function(mgr, config){
46377     Roo.LayoutRegion.call(this, mgr, config, "center");
46378     this.visible = true;
46379     this.minWidth = config.minWidth || 20;
46380     this.minHeight = config.minHeight || 20;
46381 };
46382
46383 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46384     hide : function(){
46385         // center panel can't be hidden
46386     },
46387     
46388     show : function(){
46389         // center panel can't be hidden
46390     },
46391     
46392     getMinWidth: function(){
46393         return this.minWidth;
46394     },
46395     
46396     getMinHeight: function(){
46397         return this.minHeight;
46398     }
46399 });
46400
46401
46402 Roo.NorthLayoutRegion = function(mgr, config){
46403     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46404     if(this.split){
46405         this.split.placement = Roo.SplitBar.TOP;
46406         this.split.orientation = Roo.SplitBar.VERTICAL;
46407         this.split.el.addClass("x-layout-split-v");
46408     }
46409     var size = config.initialSize || config.height;
46410     if(typeof size != "undefined"){
46411         this.el.setHeight(size);
46412     }
46413 };
46414 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46415     orientation: Roo.SplitBar.VERTICAL,
46416     getBox : function(){
46417         if(this.collapsed){
46418             return this.collapsedEl.getBox();
46419         }
46420         var box = this.el.getBox();
46421         if(this.split){
46422             box.height += this.split.el.getHeight();
46423         }
46424         return box;
46425     },
46426     
46427     updateBox : function(box){
46428         if(this.split && !this.collapsed){
46429             box.height -= this.split.el.getHeight();
46430             this.split.el.setLeft(box.x);
46431             this.split.el.setTop(box.y+box.height);
46432             this.split.el.setWidth(box.width);
46433         }
46434         if(this.collapsed){
46435             this.updateBody(box.width, null);
46436         }
46437         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46438     }
46439 });
46440
46441 Roo.SouthLayoutRegion = function(mgr, config){
46442     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46443     if(this.split){
46444         this.split.placement = Roo.SplitBar.BOTTOM;
46445         this.split.orientation = Roo.SplitBar.VERTICAL;
46446         this.split.el.addClass("x-layout-split-v");
46447     }
46448     var size = config.initialSize || config.height;
46449     if(typeof size != "undefined"){
46450         this.el.setHeight(size);
46451     }
46452 };
46453 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46454     orientation: Roo.SplitBar.VERTICAL,
46455     getBox : function(){
46456         if(this.collapsed){
46457             return this.collapsedEl.getBox();
46458         }
46459         var box = this.el.getBox();
46460         if(this.split){
46461             var sh = this.split.el.getHeight();
46462             box.height += sh;
46463             box.y -= sh;
46464         }
46465         return box;
46466     },
46467     
46468     updateBox : function(box){
46469         if(this.split && !this.collapsed){
46470             var sh = this.split.el.getHeight();
46471             box.height -= sh;
46472             box.y += sh;
46473             this.split.el.setLeft(box.x);
46474             this.split.el.setTop(box.y-sh);
46475             this.split.el.setWidth(box.width);
46476         }
46477         if(this.collapsed){
46478             this.updateBody(box.width, null);
46479         }
46480         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46481     }
46482 });
46483
46484 Roo.EastLayoutRegion = function(mgr, config){
46485     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46486     if(this.split){
46487         this.split.placement = Roo.SplitBar.RIGHT;
46488         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46489         this.split.el.addClass("x-layout-split-h");
46490     }
46491     var size = config.initialSize || config.width;
46492     if(typeof size != "undefined"){
46493         this.el.setWidth(size);
46494     }
46495 };
46496 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46497     orientation: Roo.SplitBar.HORIZONTAL,
46498     getBox : function(){
46499         if(this.collapsed){
46500             return this.collapsedEl.getBox();
46501         }
46502         var box = this.el.getBox();
46503         if(this.split){
46504             var sw = this.split.el.getWidth();
46505             box.width += sw;
46506             box.x -= sw;
46507         }
46508         return box;
46509     },
46510
46511     updateBox : function(box){
46512         if(this.split && !this.collapsed){
46513             var sw = this.split.el.getWidth();
46514             box.width -= sw;
46515             this.split.el.setLeft(box.x);
46516             this.split.el.setTop(box.y);
46517             this.split.el.setHeight(box.height);
46518             box.x += sw;
46519         }
46520         if(this.collapsed){
46521             this.updateBody(null, box.height);
46522         }
46523         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46524     }
46525 });
46526
46527 Roo.WestLayoutRegion = function(mgr, config){
46528     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46529     if(this.split){
46530         this.split.placement = Roo.SplitBar.LEFT;
46531         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46532         this.split.el.addClass("x-layout-split-h");
46533     }
46534     var size = config.initialSize || config.width;
46535     if(typeof size != "undefined"){
46536         this.el.setWidth(size);
46537     }
46538 };
46539 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46540     orientation: Roo.SplitBar.HORIZONTAL,
46541     getBox : function(){
46542         if(this.collapsed){
46543             return this.collapsedEl.getBox();
46544         }
46545         var box = this.el.getBox();
46546         if(this.split){
46547             box.width += this.split.el.getWidth();
46548         }
46549         return box;
46550     },
46551     
46552     updateBox : function(box){
46553         if(this.split && !this.collapsed){
46554             var sw = this.split.el.getWidth();
46555             box.width -= sw;
46556             this.split.el.setLeft(box.x+box.width);
46557             this.split.el.setTop(box.y);
46558             this.split.el.setHeight(box.height);
46559         }
46560         if(this.collapsed){
46561             this.updateBody(null, box.height);
46562         }
46563         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46564     }
46565 });
46566 /*
46567  * Based on:
46568  * Ext JS Library 1.1.1
46569  * Copyright(c) 2006-2007, Ext JS, LLC.
46570  *
46571  * Originally Released Under LGPL - original licence link has changed is not relivant.
46572  *
46573  * Fork - LGPL
46574  * <script type="text/javascript">
46575  */
46576  
46577  
46578 /*
46579  * Private internal class for reading and applying state
46580  */
46581 Roo.LayoutStateManager = function(layout){
46582      // default empty state
46583      this.state = {
46584         north: {},
46585         south: {},
46586         east: {},
46587         west: {}       
46588     };
46589 };
46590
46591 Roo.LayoutStateManager.prototype = {
46592     init : function(layout, provider){
46593         this.provider = provider;
46594         var state = provider.get(layout.id+"-layout-state");
46595         if(state){
46596             var wasUpdating = layout.isUpdating();
46597             if(!wasUpdating){
46598                 layout.beginUpdate();
46599             }
46600             for(var key in state){
46601                 if(typeof state[key] != "function"){
46602                     var rstate = state[key];
46603                     var r = layout.getRegion(key);
46604                     if(r && rstate){
46605                         if(rstate.size){
46606                             r.resizeTo(rstate.size);
46607                         }
46608                         if(rstate.collapsed == true){
46609                             r.collapse(true);
46610                         }else{
46611                             r.expand(null, true);
46612                         }
46613                     }
46614                 }
46615             }
46616             if(!wasUpdating){
46617                 layout.endUpdate();
46618             }
46619             this.state = state; 
46620         }
46621         this.layout = layout;
46622         layout.on("regionresized", this.onRegionResized, this);
46623         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46624         layout.on("regionexpanded", this.onRegionExpanded, this);
46625     },
46626     
46627     storeState : function(){
46628         this.provider.set(this.layout.id+"-layout-state", this.state);
46629     },
46630     
46631     onRegionResized : function(region, newSize){
46632         this.state[region.getPosition()].size = newSize;
46633         this.storeState();
46634     },
46635     
46636     onRegionCollapsed : function(region){
46637         this.state[region.getPosition()].collapsed = true;
46638         this.storeState();
46639     },
46640     
46641     onRegionExpanded : function(region){
46642         this.state[region.getPosition()].collapsed = false;
46643         this.storeState();
46644     }
46645 };/*
46646  * Based on:
46647  * Ext JS Library 1.1.1
46648  * Copyright(c) 2006-2007, Ext JS, LLC.
46649  *
46650  * Originally Released Under LGPL - original licence link has changed is not relivant.
46651  *
46652  * Fork - LGPL
46653  * <script type="text/javascript">
46654  */
46655 /**
46656  * @class Roo.ContentPanel
46657  * @extends Roo.util.Observable
46658  * A basic ContentPanel element.
46659  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46660  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46661  * @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
46662  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46663  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46664  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46665  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46666  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46667  * @cfg {String} title          The title for this panel
46668  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46669  * @cfg {String} url            Calls {@link #setUrl} with this value
46670  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46671  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46672  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46673  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46674
46675  * @constructor
46676  * Create a new ContentPanel.
46677  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46678  * @param {String/Object} config A string to set only the title or a config object
46679  * @param {String} content (optional) Set the HTML content for this panel
46680  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46681  */
46682 Roo.ContentPanel = function(el, config, content){
46683     
46684      
46685     /*
46686     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46687         config = el;
46688         el = Roo.id();
46689     }
46690     if (config && config.parentLayout) { 
46691         el = config.parentLayout.el.createChild(); 
46692     }
46693     */
46694     if(el.autoCreate){ // xtype is available if this is called from factory
46695         config = el;
46696         el = Roo.id();
46697     }
46698     this.el = Roo.get(el);
46699     if(!this.el && config && config.autoCreate){
46700         if(typeof config.autoCreate == "object"){
46701             if(!config.autoCreate.id){
46702                 config.autoCreate.id = config.id||el;
46703             }
46704             this.el = Roo.DomHelper.append(document.body,
46705                         config.autoCreate, true);
46706         }else{
46707             this.el = Roo.DomHelper.append(document.body,
46708                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46709         }
46710     }
46711     this.closable = false;
46712     this.loaded = false;
46713     this.active = false;
46714     if(typeof config == "string"){
46715         this.title = config;
46716     }else{
46717         Roo.apply(this, config);
46718     }
46719     
46720     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46721         this.wrapEl = this.el.wrap();
46722         this.toolbar.container = this.el.insertSibling(false, 'before');
46723         this.toolbar = new Roo.Toolbar(this.toolbar);
46724     }
46725     
46726     
46727     
46728     if(this.resizeEl){
46729         this.resizeEl = Roo.get(this.resizeEl, true);
46730     }else{
46731         this.resizeEl = this.el;
46732     }
46733     this.addEvents({
46734         /**
46735          * @event activate
46736          * Fires when this panel is activated. 
46737          * @param {Roo.ContentPanel} this
46738          */
46739         "activate" : true,
46740         /**
46741          * @event deactivate
46742          * Fires when this panel is activated. 
46743          * @param {Roo.ContentPanel} this
46744          */
46745         "deactivate" : true,
46746
46747         /**
46748          * @event resize
46749          * Fires when this panel is resized if fitToFrame is true.
46750          * @param {Roo.ContentPanel} this
46751          * @param {Number} width The width after any component adjustments
46752          * @param {Number} height The height after any component adjustments
46753          */
46754         "resize" : true,
46755         
46756          /**
46757          * @event render
46758          * Fires when this tab is created
46759          * @param {Roo.ContentPanel} this
46760          */
46761         "render" : true
46762         
46763         
46764         
46765     });
46766     if(this.autoScroll){
46767         this.resizeEl.setStyle("overflow", "auto");
46768     } else {
46769         // fix randome scrolling
46770         this.el.on('scroll', function() {
46771             Roo.log('fix random scolling');
46772             this.scrollTo('top',0); 
46773         });
46774     }
46775     content = content || this.content;
46776     if(content){
46777         this.setContent(content);
46778     }
46779     if(config && config.url){
46780         this.setUrl(this.url, this.params, this.loadOnce);
46781     }
46782     
46783     
46784     
46785     Roo.ContentPanel.superclass.constructor.call(this);
46786     
46787     this.fireEvent('render', this);
46788 };
46789
46790 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46791     tabTip:'',
46792     setRegion : function(region){
46793         this.region = region;
46794         if(region){
46795            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46796         }else{
46797            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46798         } 
46799     },
46800     
46801     /**
46802      * Returns the toolbar for this Panel if one was configured. 
46803      * @return {Roo.Toolbar} 
46804      */
46805     getToolbar : function(){
46806         return this.toolbar;
46807     },
46808     
46809     setActiveState : function(active){
46810         this.active = active;
46811         if(!active){
46812             this.fireEvent("deactivate", this);
46813         }else{
46814             this.fireEvent("activate", this);
46815         }
46816     },
46817     /**
46818      * Updates this panel's element
46819      * @param {String} content The new content
46820      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46821     */
46822     setContent : function(content, loadScripts){
46823         this.el.update(content, loadScripts);
46824     },
46825
46826     ignoreResize : function(w, h){
46827         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46828             return true;
46829         }else{
46830             this.lastSize = {width: w, height: h};
46831             return false;
46832         }
46833     },
46834     /**
46835      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46836      * @return {Roo.UpdateManager} The UpdateManager
46837      */
46838     getUpdateManager : function(){
46839         return this.el.getUpdateManager();
46840     },
46841      /**
46842      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46843      * @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:
46844 <pre><code>
46845 panel.load({
46846     url: "your-url.php",
46847     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46848     callback: yourFunction,
46849     scope: yourObject, //(optional scope)
46850     discardUrl: false,
46851     nocache: false,
46852     text: "Loading...",
46853     timeout: 30,
46854     scripts: false
46855 });
46856 </code></pre>
46857      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46858      * 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.
46859      * @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}
46860      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46861      * @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.
46862      * @return {Roo.ContentPanel} this
46863      */
46864     load : function(){
46865         var um = this.el.getUpdateManager();
46866         um.update.apply(um, arguments);
46867         return this;
46868     },
46869
46870
46871     /**
46872      * 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.
46873      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46874      * @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)
46875      * @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)
46876      * @return {Roo.UpdateManager} The UpdateManager
46877      */
46878     setUrl : function(url, params, loadOnce){
46879         if(this.refreshDelegate){
46880             this.removeListener("activate", this.refreshDelegate);
46881         }
46882         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46883         this.on("activate", this.refreshDelegate);
46884         return this.el.getUpdateManager();
46885     },
46886     
46887     _handleRefresh : function(url, params, loadOnce){
46888         if(!loadOnce || !this.loaded){
46889             var updater = this.el.getUpdateManager();
46890             updater.update(url, params, this._setLoaded.createDelegate(this));
46891         }
46892     },
46893     
46894     _setLoaded : function(){
46895         this.loaded = true;
46896     }, 
46897     
46898     /**
46899      * Returns this panel's id
46900      * @return {String} 
46901      */
46902     getId : function(){
46903         return this.el.id;
46904     },
46905     
46906     /** 
46907      * Returns this panel's element - used by regiosn to add.
46908      * @return {Roo.Element} 
46909      */
46910     getEl : function(){
46911         return this.wrapEl || this.el;
46912     },
46913     
46914     adjustForComponents : function(width, height){
46915         if(this.resizeEl != this.el){
46916             width -= this.el.getFrameWidth('lr');
46917             height -= this.el.getFrameWidth('tb');
46918         }
46919         if(this.toolbar){
46920             var te = this.toolbar.getEl();
46921             height -= te.getHeight();
46922             te.setWidth(width);
46923         }
46924         if(this.adjustments){
46925             width += this.adjustments[0];
46926             height += this.adjustments[1];
46927         }
46928         return {"width": width, "height": height};
46929     },
46930     
46931     setSize : function(width, height){
46932         if(this.fitToFrame && !this.ignoreResize(width, height)){
46933             if(this.fitContainer && this.resizeEl != this.el){
46934                 this.el.setSize(width, height);
46935             }
46936             var size = this.adjustForComponents(width, height);
46937             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46938             this.fireEvent('resize', this, size.width, size.height);
46939         }
46940     },
46941     
46942     /**
46943      * Returns this panel's title
46944      * @return {String} 
46945      */
46946     getTitle : function(){
46947         return this.title;
46948     },
46949     
46950     /**
46951      * Set this panel's title
46952      * @param {String} title
46953      */
46954     setTitle : function(title){
46955         this.title = title;
46956         if(this.region){
46957             this.region.updatePanelTitle(this, title);
46958         }
46959     },
46960     
46961     /**
46962      * Returns true is this panel was configured to be closable
46963      * @return {Boolean} 
46964      */
46965     isClosable : function(){
46966         return this.closable;
46967     },
46968     
46969     beforeSlide : function(){
46970         this.el.clip();
46971         this.resizeEl.clip();
46972     },
46973     
46974     afterSlide : function(){
46975         this.el.unclip();
46976         this.resizeEl.unclip();
46977     },
46978     
46979     /**
46980      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46981      *   Will fail silently if the {@link #setUrl} method has not been called.
46982      *   This does not activate the panel, just updates its content.
46983      */
46984     refresh : function(){
46985         if(this.refreshDelegate){
46986            this.loaded = false;
46987            this.refreshDelegate();
46988         }
46989     },
46990     
46991     /**
46992      * Destroys this panel
46993      */
46994     destroy : function(){
46995         this.el.removeAllListeners();
46996         var tempEl = document.createElement("span");
46997         tempEl.appendChild(this.el.dom);
46998         tempEl.innerHTML = "";
46999         this.el.remove();
47000         this.el = null;
47001     },
47002     
47003     /**
47004      * form - if the content panel contains a form - this is a reference to it.
47005      * @type {Roo.form.Form}
47006      */
47007     form : false,
47008     /**
47009      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
47010      *    This contains a reference to it.
47011      * @type {Roo.View}
47012      */
47013     view : false,
47014     
47015       /**
47016      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
47017      * <pre><code>
47018
47019 layout.addxtype({
47020        xtype : 'Form',
47021        items: [ .... ]
47022    }
47023 );
47024
47025 </code></pre>
47026      * @param {Object} cfg Xtype definition of item to add.
47027      */
47028     
47029     addxtype : function(cfg) {
47030         // add form..
47031         if (cfg.xtype.match(/^Form$/)) {
47032             var el = this.el.createChild();
47033
47034             this.form = new  Roo.form.Form(cfg);
47035             
47036             
47037             if ( this.form.allItems.length) this.form.render(el.dom);
47038             return this.form;
47039         }
47040         // should only have one of theses..
47041         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
47042             // views..
47043             cfg.el = this.el.appendChild(document.createElement("div"));
47044             // factory?
47045             
47046             var ret = new Roo.factory(cfg);
47047             ret.render && ret.render(false, ''); // render blank..
47048             this.view = ret;
47049             return ret;
47050         }
47051         return false;
47052     }
47053 });
47054
47055 /**
47056  * @class Roo.GridPanel
47057  * @extends Roo.ContentPanel
47058  * @constructor
47059  * Create a new GridPanel.
47060  * @param {Roo.grid.Grid} grid The grid for this panel
47061  * @param {String/Object} config A string to set only the panel's title, or a config object
47062  */
47063 Roo.GridPanel = function(grid, config){
47064     
47065   
47066     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
47067         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
47068         
47069     this.wrapper.dom.appendChild(grid.getGridEl().dom);
47070     
47071     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
47072     
47073     if(this.toolbar){
47074         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
47075     }
47076     // xtype created footer. - not sure if will work as we normally have to render first..
47077     if (this.footer && !this.footer.el && this.footer.xtype) {
47078         
47079         this.footer.container = this.grid.getView().getFooterPanel(true);
47080         this.footer.dataSource = this.grid.dataSource;
47081         this.footer = Roo.factory(this.footer, Roo);
47082         
47083     }
47084     
47085     grid.monitorWindowResize = false; // turn off autosizing
47086     grid.autoHeight = false;
47087     grid.autoWidth = false;
47088     this.grid = grid;
47089     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
47090 };
47091
47092 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
47093     getId : function(){
47094         return this.grid.id;
47095     },
47096     
47097     /**
47098      * Returns the grid for this panel
47099      * @return {Roo.grid.Grid} 
47100      */
47101     getGrid : function(){
47102         return this.grid;    
47103     },
47104     
47105     setSize : function(width, height){
47106         if(!this.ignoreResize(width, height)){
47107             var grid = this.grid;
47108             var size = this.adjustForComponents(width, height);
47109             grid.getGridEl().setSize(size.width, size.height);
47110             grid.autoSize();
47111         }
47112     },
47113     
47114     beforeSlide : function(){
47115         this.grid.getView().scroller.clip();
47116     },
47117     
47118     afterSlide : function(){
47119         this.grid.getView().scroller.unclip();
47120     },
47121     
47122     destroy : function(){
47123         this.grid.destroy();
47124         delete this.grid;
47125         Roo.GridPanel.superclass.destroy.call(this); 
47126     }
47127 });
47128
47129
47130 /**
47131  * @class Roo.NestedLayoutPanel
47132  * @extends Roo.ContentPanel
47133  * @constructor
47134  * Create a new NestedLayoutPanel.
47135  * 
47136  * 
47137  * @param {Roo.BorderLayout} layout The layout for this panel
47138  * @param {String/Object} config A string to set only the title or a config object
47139  */
47140 Roo.NestedLayoutPanel = function(layout, config)
47141 {
47142     // construct with only one argument..
47143     /* FIXME - implement nicer consturctors
47144     if (layout.layout) {
47145         config = layout;
47146         layout = config.layout;
47147         delete config.layout;
47148     }
47149     if (layout.xtype && !layout.getEl) {
47150         // then layout needs constructing..
47151         layout = Roo.factory(layout, Roo);
47152     }
47153     */
47154     
47155     
47156     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47157     
47158     layout.monitorWindowResize = false; // turn off autosizing
47159     this.layout = layout;
47160     this.layout.getEl().addClass("x-layout-nested-layout");
47161     
47162     
47163     
47164     
47165 };
47166
47167 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47168
47169     setSize : function(width, height){
47170         if(!this.ignoreResize(width, height)){
47171             var size = this.adjustForComponents(width, height);
47172             var el = this.layout.getEl();
47173             el.setSize(size.width, size.height);
47174             var touch = el.dom.offsetWidth;
47175             this.layout.layout();
47176             // ie requires a double layout on the first pass
47177             if(Roo.isIE && !this.initialized){
47178                 this.initialized = true;
47179                 this.layout.layout();
47180             }
47181         }
47182     },
47183     
47184     // activate all subpanels if not currently active..
47185     
47186     setActiveState : function(active){
47187         this.active = active;
47188         if(!active){
47189             this.fireEvent("deactivate", this);
47190             return;
47191         }
47192         
47193         this.fireEvent("activate", this);
47194         // not sure if this should happen before or after..
47195         if (!this.layout) {
47196             return; // should not happen..
47197         }
47198         var reg = false;
47199         for (var r in this.layout.regions) {
47200             reg = this.layout.getRegion(r);
47201             if (reg.getActivePanel()) {
47202                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47203                 reg.setActivePanel(reg.getActivePanel());
47204                 continue;
47205             }
47206             if (!reg.panels.length) {
47207                 continue;
47208             }
47209             reg.showPanel(reg.getPanel(0));
47210         }
47211         
47212         
47213         
47214         
47215     },
47216     
47217     /**
47218      * Returns the nested BorderLayout for this panel
47219      * @return {Roo.BorderLayout} 
47220      */
47221     getLayout : function(){
47222         return this.layout;
47223     },
47224     
47225      /**
47226      * Adds a xtype elements to the layout of the nested panel
47227      * <pre><code>
47228
47229 panel.addxtype({
47230        xtype : 'ContentPanel',
47231        region: 'west',
47232        items: [ .... ]
47233    }
47234 );
47235
47236 panel.addxtype({
47237         xtype : 'NestedLayoutPanel',
47238         region: 'west',
47239         layout: {
47240            center: { },
47241            west: { }   
47242         },
47243         items : [ ... list of content panels or nested layout panels.. ]
47244    }
47245 );
47246 </code></pre>
47247      * @param {Object} cfg Xtype definition of item to add.
47248      */
47249     addxtype : function(cfg) {
47250         return this.layout.addxtype(cfg);
47251     
47252     }
47253 });
47254
47255 Roo.ScrollPanel = function(el, config, content){
47256     config = config || {};
47257     config.fitToFrame = true;
47258     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47259     
47260     this.el.dom.style.overflow = "hidden";
47261     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47262     this.el.removeClass("x-layout-inactive-content");
47263     this.el.on("mousewheel", this.onWheel, this);
47264
47265     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47266     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47267     up.unselectable(); down.unselectable();
47268     up.on("click", this.scrollUp, this);
47269     down.on("click", this.scrollDown, this);
47270     up.addClassOnOver("x-scroller-btn-over");
47271     down.addClassOnOver("x-scroller-btn-over");
47272     up.addClassOnClick("x-scroller-btn-click");
47273     down.addClassOnClick("x-scroller-btn-click");
47274     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47275
47276     this.resizeEl = this.el;
47277     this.el = wrap; this.up = up; this.down = down;
47278 };
47279
47280 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47281     increment : 100,
47282     wheelIncrement : 5,
47283     scrollUp : function(){
47284         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47285     },
47286
47287     scrollDown : function(){
47288         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47289     },
47290
47291     afterScroll : function(){
47292         var el = this.resizeEl;
47293         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47294         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47295         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47296     },
47297
47298     setSize : function(){
47299         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47300         this.afterScroll();
47301     },
47302
47303     onWheel : function(e){
47304         var d = e.getWheelDelta();
47305         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47306         this.afterScroll();
47307         e.stopEvent();
47308     },
47309
47310     setContent : function(content, loadScripts){
47311         this.resizeEl.update(content, loadScripts);
47312     }
47313
47314 });
47315
47316
47317
47318
47319
47320
47321
47322
47323
47324 /**
47325  * @class Roo.TreePanel
47326  * @extends Roo.ContentPanel
47327  * @constructor
47328  * Create a new TreePanel. - defaults to fit/scoll contents.
47329  * @param {String/Object} config A string to set only the panel's title, or a config object
47330  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47331  */
47332 Roo.TreePanel = function(config){
47333     var el = config.el;
47334     var tree = config.tree;
47335     delete config.tree; 
47336     delete config.el; // hopefull!
47337     
47338     // wrapper for IE7 strict & safari scroll issue
47339     
47340     var treeEl = el.createChild();
47341     config.resizeEl = treeEl;
47342     
47343     
47344     
47345     Roo.TreePanel.superclass.constructor.call(this, el, config);
47346  
47347  
47348     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47349     //console.log(tree);
47350     this.on('activate', function()
47351     {
47352         if (this.tree.rendered) {
47353             return;
47354         }
47355         //console.log('render tree');
47356         this.tree.render();
47357     });
47358     
47359     this.on('resize',  function (cp, w, h) {
47360             this.tree.innerCt.setWidth(w);
47361             this.tree.innerCt.setHeight(h);
47362             this.tree.innerCt.setStyle('overflow-y', 'auto');
47363     });
47364
47365         
47366     
47367 };
47368
47369 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47370     fitToFrame : true,
47371     autoScroll : true
47372 });
47373
47374
47375
47376
47377
47378
47379
47380
47381
47382
47383
47384 /*
47385  * Based on:
47386  * Ext JS Library 1.1.1
47387  * Copyright(c) 2006-2007, Ext JS, LLC.
47388  *
47389  * Originally Released Under LGPL - original licence link has changed is not relivant.
47390  *
47391  * Fork - LGPL
47392  * <script type="text/javascript">
47393  */
47394  
47395
47396 /**
47397  * @class Roo.ReaderLayout
47398  * @extends Roo.BorderLayout
47399  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47400  * center region containing two nested regions (a top one for a list view and one for item preview below),
47401  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47402  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47403  * expedites the setup of the overall layout and regions for this common application style.
47404  * Example:
47405  <pre><code>
47406 var reader = new Roo.ReaderLayout();
47407 var CP = Roo.ContentPanel;  // shortcut for adding
47408
47409 reader.beginUpdate();
47410 reader.add("north", new CP("north", "North"));
47411 reader.add("west", new CP("west", {title: "West"}));
47412 reader.add("east", new CP("east", {title: "East"}));
47413
47414 reader.regions.listView.add(new CP("listView", "List"));
47415 reader.regions.preview.add(new CP("preview", "Preview"));
47416 reader.endUpdate();
47417 </code></pre>
47418 * @constructor
47419 * Create a new ReaderLayout
47420 * @param {Object} config Configuration options
47421 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47422 * document.body if omitted)
47423 */
47424 Roo.ReaderLayout = function(config, renderTo){
47425     var c = config || {size:{}};
47426     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47427         north: c.north !== false ? Roo.apply({
47428             split:false,
47429             initialSize: 32,
47430             titlebar: false
47431         }, c.north) : false,
47432         west: c.west !== false ? Roo.apply({
47433             split:true,
47434             initialSize: 200,
47435             minSize: 175,
47436             maxSize: 400,
47437             titlebar: true,
47438             collapsible: true,
47439             animate: true,
47440             margins:{left:5,right:0,bottom:5,top:5},
47441             cmargins:{left:5,right:5,bottom:5,top:5}
47442         }, c.west) : false,
47443         east: c.east !== false ? Roo.apply({
47444             split:true,
47445             initialSize: 200,
47446             minSize: 175,
47447             maxSize: 400,
47448             titlebar: true,
47449             collapsible: true,
47450             animate: true,
47451             margins:{left:0,right:5,bottom:5,top:5},
47452             cmargins:{left:5,right:5,bottom:5,top:5}
47453         }, c.east) : false,
47454         center: Roo.apply({
47455             tabPosition: 'top',
47456             autoScroll:false,
47457             closeOnTab: true,
47458             titlebar:false,
47459             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47460         }, c.center)
47461     });
47462
47463     this.el.addClass('x-reader');
47464
47465     this.beginUpdate();
47466
47467     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47468         south: c.preview !== false ? Roo.apply({
47469             split:true,
47470             initialSize: 200,
47471             minSize: 100,
47472             autoScroll:true,
47473             collapsible:true,
47474             titlebar: true,
47475             cmargins:{top:5,left:0, right:0, bottom:0}
47476         }, c.preview) : false,
47477         center: Roo.apply({
47478             autoScroll:false,
47479             titlebar:false,
47480             minHeight:200
47481         }, c.listView)
47482     });
47483     this.add('center', new Roo.NestedLayoutPanel(inner,
47484             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47485
47486     this.endUpdate();
47487
47488     this.regions.preview = inner.getRegion('south');
47489     this.regions.listView = inner.getRegion('center');
47490 };
47491
47492 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47493  * Based on:
47494  * Ext JS Library 1.1.1
47495  * Copyright(c) 2006-2007, Ext JS, LLC.
47496  *
47497  * Originally Released Under LGPL - original licence link has changed is not relivant.
47498  *
47499  * Fork - LGPL
47500  * <script type="text/javascript">
47501  */
47502  
47503 /**
47504  * @class Roo.grid.Grid
47505  * @extends Roo.util.Observable
47506  * This class represents the primary interface of a component based grid control.
47507  * <br><br>Usage:<pre><code>
47508  var grid = new Roo.grid.Grid("my-container-id", {
47509      ds: myDataStore,
47510      cm: myColModel,
47511      selModel: mySelectionModel,
47512      autoSizeColumns: true,
47513      monitorWindowResize: false,
47514      trackMouseOver: true
47515  });
47516  // set any options
47517  grid.render();
47518  * </code></pre>
47519  * <b>Common Problems:</b><br/>
47520  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47521  * element will correct this<br/>
47522  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47523  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47524  * are unpredictable.<br/>
47525  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47526  * grid to calculate dimensions/offsets.<br/>
47527   * @constructor
47528  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47529  * The container MUST have some type of size defined for the grid to fill. The container will be
47530  * automatically set to position relative if it isn't already.
47531  * @param {Object} config A config object that sets properties on this grid.
47532  */
47533 Roo.grid.Grid = function(container, config){
47534         // initialize the container
47535         this.container = Roo.get(container);
47536         this.container.update("");
47537         this.container.setStyle("overflow", "hidden");
47538     this.container.addClass('x-grid-container');
47539
47540     this.id = this.container.id;
47541
47542     Roo.apply(this, config);
47543     // check and correct shorthanded configs
47544     if(this.ds){
47545         this.dataSource = this.ds;
47546         delete this.ds;
47547     }
47548     if(this.cm){
47549         this.colModel = this.cm;
47550         delete this.cm;
47551     }
47552     if(this.sm){
47553         this.selModel = this.sm;
47554         delete this.sm;
47555     }
47556
47557     if (this.selModel) {
47558         this.selModel = Roo.factory(this.selModel, Roo.grid);
47559         this.sm = this.selModel;
47560         this.sm.xmodule = this.xmodule || false;
47561     }
47562     if (typeof(this.colModel.config) == 'undefined') {
47563         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47564         this.cm = this.colModel;
47565         this.cm.xmodule = this.xmodule || false;
47566     }
47567     if (this.dataSource) {
47568         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47569         this.ds = this.dataSource;
47570         this.ds.xmodule = this.xmodule || false;
47571          
47572     }
47573     
47574     
47575     
47576     if(this.width){
47577         this.container.setWidth(this.width);
47578     }
47579
47580     if(this.height){
47581         this.container.setHeight(this.height);
47582     }
47583     /** @private */
47584         this.addEvents({
47585         // raw events
47586         /**
47587          * @event click
47588          * The raw click event for the entire grid.
47589          * @param {Roo.EventObject} e
47590          */
47591         "click" : true,
47592         /**
47593          * @event dblclick
47594          * The raw dblclick event for the entire grid.
47595          * @param {Roo.EventObject} e
47596          */
47597         "dblclick" : true,
47598         /**
47599          * @event contextmenu
47600          * The raw contextmenu event for the entire grid.
47601          * @param {Roo.EventObject} e
47602          */
47603         "contextmenu" : true,
47604         /**
47605          * @event mousedown
47606          * The raw mousedown event for the entire grid.
47607          * @param {Roo.EventObject} e
47608          */
47609         "mousedown" : true,
47610         /**
47611          * @event mouseup
47612          * The raw mouseup event for the entire grid.
47613          * @param {Roo.EventObject} e
47614          */
47615         "mouseup" : true,
47616         /**
47617          * @event mouseover
47618          * The raw mouseover event for the entire grid.
47619          * @param {Roo.EventObject} e
47620          */
47621         "mouseover" : true,
47622         /**
47623          * @event mouseout
47624          * The raw mouseout event for the entire grid.
47625          * @param {Roo.EventObject} e
47626          */
47627         "mouseout" : true,
47628         /**
47629          * @event keypress
47630          * The raw keypress event for the entire grid.
47631          * @param {Roo.EventObject} e
47632          */
47633         "keypress" : true,
47634         /**
47635          * @event keydown
47636          * The raw keydown event for the entire grid.
47637          * @param {Roo.EventObject} e
47638          */
47639         "keydown" : true,
47640
47641         // custom events
47642
47643         /**
47644          * @event cellclick
47645          * Fires when a cell is clicked
47646          * @param {Grid} this
47647          * @param {Number} rowIndex
47648          * @param {Number} columnIndex
47649          * @param {Roo.EventObject} e
47650          */
47651         "cellclick" : true,
47652         /**
47653          * @event celldblclick
47654          * Fires when a cell is double clicked
47655          * @param {Grid} this
47656          * @param {Number} rowIndex
47657          * @param {Number} columnIndex
47658          * @param {Roo.EventObject} e
47659          */
47660         "celldblclick" : true,
47661         /**
47662          * @event rowclick
47663          * Fires when a row is clicked
47664          * @param {Grid} this
47665          * @param {Number} rowIndex
47666          * @param {Roo.EventObject} e
47667          */
47668         "rowclick" : true,
47669         /**
47670          * @event rowdblclick
47671          * Fires when a row is double clicked
47672          * @param {Grid} this
47673          * @param {Number} rowIndex
47674          * @param {Roo.EventObject} e
47675          */
47676         "rowdblclick" : true,
47677         /**
47678          * @event headerclick
47679          * Fires when a header is clicked
47680          * @param {Grid} this
47681          * @param {Number} columnIndex
47682          * @param {Roo.EventObject} e
47683          */
47684         "headerclick" : true,
47685         /**
47686          * @event headerdblclick
47687          * Fires when a header cell is double clicked
47688          * @param {Grid} this
47689          * @param {Number} columnIndex
47690          * @param {Roo.EventObject} e
47691          */
47692         "headerdblclick" : true,
47693         /**
47694          * @event rowcontextmenu
47695          * Fires when a row is right clicked
47696          * @param {Grid} this
47697          * @param {Number} rowIndex
47698          * @param {Roo.EventObject} e
47699          */
47700         "rowcontextmenu" : true,
47701         /**
47702          * @event cellcontextmenu
47703          * Fires when a cell is right clicked
47704          * @param {Grid} this
47705          * @param {Number} rowIndex
47706          * @param {Number} cellIndex
47707          * @param {Roo.EventObject} e
47708          */
47709          "cellcontextmenu" : true,
47710         /**
47711          * @event headercontextmenu
47712          * Fires when a header is right clicked
47713          * @param {Grid} this
47714          * @param {Number} columnIndex
47715          * @param {Roo.EventObject} e
47716          */
47717         "headercontextmenu" : true,
47718         /**
47719          * @event bodyscroll
47720          * Fires when the body element is scrolled
47721          * @param {Number} scrollLeft
47722          * @param {Number} scrollTop
47723          */
47724         "bodyscroll" : true,
47725         /**
47726          * @event columnresize
47727          * Fires when the user resizes a column
47728          * @param {Number} columnIndex
47729          * @param {Number} newSize
47730          */
47731         "columnresize" : true,
47732         /**
47733          * @event columnmove
47734          * Fires when the user moves a column
47735          * @param {Number} oldIndex
47736          * @param {Number} newIndex
47737          */
47738         "columnmove" : true,
47739         /**
47740          * @event startdrag
47741          * Fires when row(s) start being dragged
47742          * @param {Grid} this
47743          * @param {Roo.GridDD} dd The drag drop object
47744          * @param {event} e The raw browser event
47745          */
47746         "startdrag" : true,
47747         /**
47748          * @event enddrag
47749          * Fires when a drag operation is complete
47750          * @param {Grid} this
47751          * @param {Roo.GridDD} dd The drag drop object
47752          * @param {event} e The raw browser event
47753          */
47754         "enddrag" : true,
47755         /**
47756          * @event dragdrop
47757          * Fires when dragged row(s) are dropped on a valid DD target
47758          * @param {Grid} this
47759          * @param {Roo.GridDD} dd The drag drop object
47760          * @param {String} targetId The target drag drop object
47761          * @param {event} e The raw browser event
47762          */
47763         "dragdrop" : true,
47764         /**
47765          * @event dragover
47766          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47767          * @param {Grid} this
47768          * @param {Roo.GridDD} dd The drag drop object
47769          * @param {String} targetId The target drag drop object
47770          * @param {event} e The raw browser event
47771          */
47772         "dragover" : true,
47773         /**
47774          * @event dragenter
47775          *  Fires when the dragged row(s) first cross another DD target while being dragged
47776          * @param {Grid} this
47777          * @param {Roo.GridDD} dd The drag drop object
47778          * @param {String} targetId The target drag drop object
47779          * @param {event} e The raw browser event
47780          */
47781         "dragenter" : true,
47782         /**
47783          * @event dragout
47784          * Fires when the dragged row(s) leave another DD target while being dragged
47785          * @param {Grid} this
47786          * @param {Roo.GridDD} dd The drag drop object
47787          * @param {String} targetId The target drag drop object
47788          * @param {event} e The raw browser event
47789          */
47790         "dragout" : true,
47791         /**
47792          * @event rowclass
47793          * Fires when a row is rendered, so you can change add a style to it.
47794          * @param {GridView} gridview   The grid view
47795          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47796          */
47797         'rowclass' : true,
47798
47799         /**
47800          * @event render
47801          * Fires when the grid is rendered
47802          * @param {Grid} grid
47803          */
47804         'render' : true
47805     });
47806
47807     Roo.grid.Grid.superclass.constructor.call(this);
47808 };
47809 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47810     
47811     /**
47812      * @cfg {String} ddGroup - drag drop group.
47813      */
47814
47815     /**
47816      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47817      */
47818     minColumnWidth : 25,
47819
47820     /**
47821      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47822      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47823      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47824      */
47825     autoSizeColumns : false,
47826
47827     /**
47828      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47829      */
47830     autoSizeHeaders : true,
47831
47832     /**
47833      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47834      */
47835     monitorWindowResize : true,
47836
47837     /**
47838      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47839      * rows measured to get a columns size. Default is 0 (all rows).
47840      */
47841     maxRowsToMeasure : 0,
47842
47843     /**
47844      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47845      */
47846     trackMouseOver : true,
47847
47848     /**
47849     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47850     */
47851     
47852     /**
47853     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47854     */
47855     enableDragDrop : false,
47856     
47857     /**
47858     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47859     */
47860     enableColumnMove : true,
47861     
47862     /**
47863     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47864     */
47865     enableColumnHide : true,
47866     
47867     /**
47868     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47869     */
47870     enableRowHeightSync : false,
47871     
47872     /**
47873     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47874     */
47875     stripeRows : true,
47876     
47877     /**
47878     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47879     */
47880     autoHeight : false,
47881
47882     /**
47883      * @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.
47884      */
47885     autoExpandColumn : false,
47886
47887     /**
47888     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47889     * Default is 50.
47890     */
47891     autoExpandMin : 50,
47892
47893     /**
47894     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47895     */
47896     autoExpandMax : 1000,
47897
47898     /**
47899     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47900     */
47901     view : null,
47902
47903     /**
47904     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47905     */
47906     loadMask : false,
47907     /**
47908     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47909     */
47910     dropTarget: false,
47911     
47912    
47913     
47914     // private
47915     rendered : false,
47916
47917     /**
47918     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47919     * of a fixed width. Default is false.
47920     */
47921     /**
47922     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47923     */
47924     /**
47925      * Called once after all setup has been completed and the grid is ready to be rendered.
47926      * @return {Roo.grid.Grid} this
47927      */
47928     render : function()
47929     {
47930         var c = this.container;
47931         // try to detect autoHeight/width mode
47932         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47933             this.autoHeight = true;
47934         }
47935         var view = this.getView();
47936         view.init(this);
47937
47938         c.on("click", this.onClick, this);
47939         c.on("dblclick", this.onDblClick, this);
47940         c.on("contextmenu", this.onContextMenu, this);
47941         c.on("keydown", this.onKeyDown, this);
47942
47943         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47944
47945         this.getSelectionModel().init(this);
47946
47947         view.render();
47948
47949         if(this.loadMask){
47950             this.loadMask = new Roo.LoadMask(this.container,
47951                     Roo.apply({store:this.dataSource}, this.loadMask));
47952         }
47953         
47954         
47955         if (this.toolbar && this.toolbar.xtype) {
47956             this.toolbar.container = this.getView().getHeaderPanel(true);
47957             this.toolbar = new Roo.Toolbar(this.toolbar);
47958         }
47959         if (this.footer && this.footer.xtype) {
47960             this.footer.dataSource = this.getDataSource();
47961             this.footer.container = this.getView().getFooterPanel(true);
47962             this.footer = Roo.factory(this.footer, Roo);
47963         }
47964         if (this.dropTarget && this.dropTarget.xtype) {
47965             delete this.dropTarget.xtype;
47966             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47967         }
47968         
47969         
47970         this.rendered = true;
47971         this.fireEvent('render', this);
47972         return this;
47973     },
47974
47975         /**
47976          * Reconfigures the grid to use a different Store and Column Model.
47977          * The View will be bound to the new objects and refreshed.
47978          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47979          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47980          */
47981     reconfigure : function(dataSource, colModel){
47982         if(this.loadMask){
47983             this.loadMask.destroy();
47984             this.loadMask = new Roo.LoadMask(this.container,
47985                     Roo.apply({store:dataSource}, this.loadMask));
47986         }
47987         this.view.bind(dataSource, colModel);
47988         this.dataSource = dataSource;
47989         this.colModel = colModel;
47990         this.view.refresh(true);
47991     },
47992
47993     // private
47994     onKeyDown : function(e){
47995         this.fireEvent("keydown", e);
47996     },
47997
47998     /**
47999      * Destroy this grid.
48000      * @param {Boolean} removeEl True to remove the element
48001      */
48002     destroy : function(removeEl, keepListeners){
48003         if(this.loadMask){
48004             this.loadMask.destroy();
48005         }
48006         var c = this.container;
48007         c.removeAllListeners();
48008         this.view.destroy();
48009         this.colModel.purgeListeners();
48010         if(!keepListeners){
48011             this.purgeListeners();
48012         }
48013         c.update("");
48014         if(removeEl === true){
48015             c.remove();
48016         }
48017     },
48018
48019     // private
48020     processEvent : function(name, e){
48021         this.fireEvent(name, e);
48022         var t = e.getTarget();
48023         var v = this.view;
48024         var header = v.findHeaderIndex(t);
48025         if(header !== false){
48026             this.fireEvent("header" + name, this, header, e);
48027         }else{
48028             var row = v.findRowIndex(t);
48029             var cell = v.findCellIndex(t);
48030             if(row !== false){
48031                 this.fireEvent("row" + name, this, row, e);
48032                 if(cell !== false){
48033                     this.fireEvent("cell" + name, this, row, cell, e);
48034                 }
48035             }
48036         }
48037     },
48038
48039     // private
48040     onClick : function(e){
48041         this.processEvent("click", e);
48042     },
48043
48044     // private
48045     onContextMenu : function(e, t){
48046         this.processEvent("contextmenu", e);
48047     },
48048
48049     // private
48050     onDblClick : function(e){
48051         this.processEvent("dblclick", e);
48052     },
48053
48054     // private
48055     walkCells : function(row, col, step, fn, scope){
48056         var cm = this.colModel, clen = cm.getColumnCount();
48057         var ds = this.dataSource, rlen = ds.getCount(), first = true;
48058         if(step < 0){
48059             if(col < 0){
48060                 row--;
48061                 first = false;
48062             }
48063             while(row >= 0){
48064                 if(!first){
48065                     col = clen-1;
48066                 }
48067                 first = false;
48068                 while(col >= 0){
48069                     if(fn.call(scope || this, row, col, cm) === true){
48070                         return [row, col];
48071                     }
48072                     col--;
48073                 }
48074                 row--;
48075             }
48076         } else {
48077             if(col >= clen){
48078                 row++;
48079                 first = false;
48080             }
48081             while(row < rlen){
48082                 if(!first){
48083                     col = 0;
48084                 }
48085                 first = false;
48086                 while(col < clen){
48087                     if(fn.call(scope || this, row, col, cm) === true){
48088                         return [row, col];
48089                     }
48090                     col++;
48091                 }
48092                 row++;
48093             }
48094         }
48095         return null;
48096     },
48097
48098     // private
48099     getSelections : function(){
48100         return this.selModel.getSelections();
48101     },
48102
48103     /**
48104      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
48105      * but if manual update is required this method will initiate it.
48106      */
48107     autoSize : function(){
48108         if(this.rendered){
48109             this.view.layout();
48110             if(this.view.adjustForScroll){
48111                 this.view.adjustForScroll();
48112             }
48113         }
48114     },
48115
48116     /**
48117      * Returns the grid's underlying element.
48118      * @return {Element} The element
48119      */
48120     getGridEl : function(){
48121         return this.container;
48122     },
48123
48124     // private for compatibility, overridden by editor grid
48125     stopEditing : function(){},
48126
48127     /**
48128      * Returns the grid's SelectionModel.
48129      * @return {SelectionModel}
48130      */
48131     getSelectionModel : function(){
48132         if(!this.selModel){
48133             this.selModel = new Roo.grid.RowSelectionModel();
48134         }
48135         return this.selModel;
48136     },
48137
48138     /**
48139      * Returns the grid's DataSource.
48140      * @return {DataSource}
48141      */
48142     getDataSource : function(){
48143         return this.dataSource;
48144     },
48145
48146     /**
48147      * Returns the grid's ColumnModel.
48148      * @return {ColumnModel}
48149      */
48150     getColumnModel : function(){
48151         return this.colModel;
48152     },
48153
48154     /**
48155      * Returns the grid's GridView object.
48156      * @return {GridView}
48157      */
48158     getView : function(){
48159         if(!this.view){
48160             this.view = new Roo.grid.GridView(this.viewConfig);
48161         }
48162         return this.view;
48163     },
48164     /**
48165      * Called to get grid's drag proxy text, by default returns this.ddText.
48166      * @return {String}
48167      */
48168     getDragDropText : function(){
48169         var count = this.selModel.getCount();
48170         return String.format(this.ddText, count, count == 1 ? '' : 's');
48171     }
48172 });
48173 /**
48174  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48175  * %0 is replaced with the number of selected rows.
48176  * @type String
48177  */
48178 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48179  * Based on:
48180  * Ext JS Library 1.1.1
48181  * Copyright(c) 2006-2007, Ext JS, LLC.
48182  *
48183  * Originally Released Under LGPL - original licence link has changed is not relivant.
48184  *
48185  * Fork - LGPL
48186  * <script type="text/javascript">
48187  */
48188  
48189 Roo.grid.AbstractGridView = function(){
48190         this.grid = null;
48191         
48192         this.events = {
48193             "beforerowremoved" : true,
48194             "beforerowsinserted" : true,
48195             "beforerefresh" : true,
48196             "rowremoved" : true,
48197             "rowsinserted" : true,
48198             "rowupdated" : true,
48199             "refresh" : true
48200         };
48201     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48202 };
48203
48204 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48205     rowClass : "x-grid-row",
48206     cellClass : "x-grid-cell",
48207     tdClass : "x-grid-td",
48208     hdClass : "x-grid-hd",
48209     splitClass : "x-grid-hd-split",
48210     
48211         init: function(grid){
48212         this.grid = grid;
48213                 var cid = this.grid.getGridEl().id;
48214         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48215         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48216         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48217         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48218         },
48219         
48220         getColumnRenderers : function(){
48221         var renderers = [];
48222         var cm = this.grid.colModel;
48223         var colCount = cm.getColumnCount();
48224         for(var i = 0; i < colCount; i++){
48225             renderers[i] = cm.getRenderer(i);
48226         }
48227         return renderers;
48228     },
48229     
48230     getColumnIds : function(){
48231         var ids = [];
48232         var cm = this.grid.colModel;
48233         var colCount = cm.getColumnCount();
48234         for(var i = 0; i < colCount; i++){
48235             ids[i] = cm.getColumnId(i);
48236         }
48237         return ids;
48238     },
48239     
48240     getDataIndexes : function(){
48241         if(!this.indexMap){
48242             this.indexMap = this.buildIndexMap();
48243         }
48244         return this.indexMap.colToData;
48245     },
48246     
48247     getColumnIndexByDataIndex : function(dataIndex){
48248         if(!this.indexMap){
48249             this.indexMap = this.buildIndexMap();
48250         }
48251         return this.indexMap.dataToCol[dataIndex];
48252     },
48253     
48254     /**
48255      * Set a css style for a column dynamically. 
48256      * @param {Number} colIndex The index of the column
48257      * @param {String} name The css property name
48258      * @param {String} value The css value
48259      */
48260     setCSSStyle : function(colIndex, name, value){
48261         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48262         Roo.util.CSS.updateRule(selector, name, value);
48263     },
48264     
48265     generateRules : function(cm){
48266         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48267         Roo.util.CSS.removeStyleSheet(rulesId);
48268         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48269             var cid = cm.getColumnId(i);
48270             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48271                          this.tdSelector, cid, " {\n}\n",
48272                          this.hdSelector, cid, " {\n}\n",
48273                          this.splitSelector, cid, " {\n}\n");
48274         }
48275         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48276     }
48277 });/*
48278  * Based on:
48279  * Ext JS Library 1.1.1
48280  * Copyright(c) 2006-2007, Ext JS, LLC.
48281  *
48282  * Originally Released Under LGPL - original licence link has changed is not relivant.
48283  *
48284  * Fork - LGPL
48285  * <script type="text/javascript">
48286  */
48287
48288 // private
48289 // This is a support class used internally by the Grid components
48290 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48291     this.grid = grid;
48292     this.view = grid.getView();
48293     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48294     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48295     if(hd2){
48296         this.setHandleElId(Roo.id(hd));
48297         this.setOuterHandleElId(Roo.id(hd2));
48298     }
48299     this.scroll = false;
48300 };
48301 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48302     maxDragWidth: 120,
48303     getDragData : function(e){
48304         var t = Roo.lib.Event.getTarget(e);
48305         var h = this.view.findHeaderCell(t);
48306         if(h){
48307             return {ddel: h.firstChild, header:h};
48308         }
48309         return false;
48310     },
48311
48312     onInitDrag : function(e){
48313         this.view.headersDisabled = true;
48314         var clone = this.dragData.ddel.cloneNode(true);
48315         clone.id = Roo.id();
48316         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48317         this.proxy.update(clone);
48318         return true;
48319     },
48320
48321     afterValidDrop : function(){
48322         var v = this.view;
48323         setTimeout(function(){
48324             v.headersDisabled = false;
48325         }, 50);
48326     },
48327
48328     afterInvalidDrop : function(){
48329         var v = this.view;
48330         setTimeout(function(){
48331             v.headersDisabled = false;
48332         }, 50);
48333     }
48334 });
48335 /*
48336  * Based on:
48337  * Ext JS Library 1.1.1
48338  * Copyright(c) 2006-2007, Ext JS, LLC.
48339  *
48340  * Originally Released Under LGPL - original licence link has changed is not relivant.
48341  *
48342  * Fork - LGPL
48343  * <script type="text/javascript">
48344  */
48345 // private
48346 // This is a support class used internally by the Grid components
48347 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48348     this.grid = grid;
48349     this.view = grid.getView();
48350     // split the proxies so they don't interfere with mouse events
48351     this.proxyTop = Roo.DomHelper.append(document.body, {
48352         cls:"col-move-top", html:"&#160;"
48353     }, true);
48354     this.proxyBottom = Roo.DomHelper.append(document.body, {
48355         cls:"col-move-bottom", html:"&#160;"
48356     }, true);
48357     this.proxyTop.hide = this.proxyBottom.hide = function(){
48358         this.setLeftTop(-100,-100);
48359         this.setStyle("visibility", "hidden");
48360     };
48361     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48362     // temporarily disabled
48363     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48364     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48365 };
48366 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48367     proxyOffsets : [-4, -9],
48368     fly: Roo.Element.fly,
48369
48370     getTargetFromEvent : function(e){
48371         var t = Roo.lib.Event.getTarget(e);
48372         var cindex = this.view.findCellIndex(t);
48373         if(cindex !== false){
48374             return this.view.getHeaderCell(cindex);
48375         }
48376         return null;
48377     },
48378
48379     nextVisible : function(h){
48380         var v = this.view, cm = this.grid.colModel;
48381         h = h.nextSibling;
48382         while(h){
48383             if(!cm.isHidden(v.getCellIndex(h))){
48384                 return h;
48385             }
48386             h = h.nextSibling;
48387         }
48388         return null;
48389     },
48390
48391     prevVisible : function(h){
48392         var v = this.view, cm = this.grid.colModel;
48393         h = h.prevSibling;
48394         while(h){
48395             if(!cm.isHidden(v.getCellIndex(h))){
48396                 return h;
48397             }
48398             h = h.prevSibling;
48399         }
48400         return null;
48401     },
48402
48403     positionIndicator : function(h, n, e){
48404         var x = Roo.lib.Event.getPageX(e);
48405         var r = Roo.lib.Dom.getRegion(n.firstChild);
48406         var px, pt, py = r.top + this.proxyOffsets[1];
48407         if((r.right - x) <= (r.right-r.left)/2){
48408             px = r.right+this.view.borderWidth;
48409             pt = "after";
48410         }else{
48411             px = r.left;
48412             pt = "before";
48413         }
48414         var oldIndex = this.view.getCellIndex(h);
48415         var newIndex = this.view.getCellIndex(n);
48416
48417         if(this.grid.colModel.isFixed(newIndex)){
48418             return false;
48419         }
48420
48421         var locked = this.grid.colModel.isLocked(newIndex);
48422
48423         if(pt == "after"){
48424             newIndex++;
48425         }
48426         if(oldIndex < newIndex){
48427             newIndex--;
48428         }
48429         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48430             return false;
48431         }
48432         px +=  this.proxyOffsets[0];
48433         this.proxyTop.setLeftTop(px, py);
48434         this.proxyTop.show();
48435         if(!this.bottomOffset){
48436             this.bottomOffset = this.view.mainHd.getHeight();
48437         }
48438         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48439         this.proxyBottom.show();
48440         return pt;
48441     },
48442
48443     onNodeEnter : function(n, dd, e, data){
48444         if(data.header != n){
48445             this.positionIndicator(data.header, n, e);
48446         }
48447     },
48448
48449     onNodeOver : function(n, dd, e, data){
48450         var result = false;
48451         if(data.header != n){
48452             result = this.positionIndicator(data.header, n, e);
48453         }
48454         if(!result){
48455             this.proxyTop.hide();
48456             this.proxyBottom.hide();
48457         }
48458         return result ? this.dropAllowed : this.dropNotAllowed;
48459     },
48460
48461     onNodeOut : function(n, dd, e, data){
48462         this.proxyTop.hide();
48463         this.proxyBottom.hide();
48464     },
48465
48466     onNodeDrop : function(n, dd, e, data){
48467         var h = data.header;
48468         if(h != n){
48469             var cm = this.grid.colModel;
48470             var x = Roo.lib.Event.getPageX(e);
48471             var r = Roo.lib.Dom.getRegion(n.firstChild);
48472             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48473             var oldIndex = this.view.getCellIndex(h);
48474             var newIndex = this.view.getCellIndex(n);
48475             var locked = cm.isLocked(newIndex);
48476             if(pt == "after"){
48477                 newIndex++;
48478             }
48479             if(oldIndex < newIndex){
48480                 newIndex--;
48481             }
48482             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48483                 return false;
48484             }
48485             cm.setLocked(oldIndex, locked, true);
48486             cm.moveColumn(oldIndex, newIndex);
48487             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48488             return true;
48489         }
48490         return false;
48491     }
48492 });
48493 /*
48494  * Based on:
48495  * Ext JS Library 1.1.1
48496  * Copyright(c) 2006-2007, Ext JS, LLC.
48497  *
48498  * Originally Released Under LGPL - original licence link has changed is not relivant.
48499  *
48500  * Fork - LGPL
48501  * <script type="text/javascript">
48502  */
48503   
48504 /**
48505  * @class Roo.grid.GridView
48506  * @extends Roo.util.Observable
48507  *
48508  * @constructor
48509  * @param {Object} config
48510  */
48511 Roo.grid.GridView = function(config){
48512     Roo.grid.GridView.superclass.constructor.call(this);
48513     this.el = null;
48514
48515     Roo.apply(this, config);
48516 };
48517
48518 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48519
48520     
48521     rowClass : "x-grid-row",
48522
48523     cellClass : "x-grid-col",
48524
48525     tdClass : "x-grid-td",
48526
48527     hdClass : "x-grid-hd",
48528
48529     splitClass : "x-grid-split",
48530
48531     sortClasses : ["sort-asc", "sort-desc"],
48532
48533     enableMoveAnim : false,
48534
48535     hlColor: "C3DAF9",
48536
48537     dh : Roo.DomHelper,
48538
48539     fly : Roo.Element.fly,
48540
48541     css : Roo.util.CSS,
48542
48543     borderWidth: 1,
48544
48545     splitOffset: 3,
48546
48547     scrollIncrement : 22,
48548
48549     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48550
48551     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48552
48553     bind : function(ds, cm){
48554         if(this.ds){
48555             this.ds.un("load", this.onLoad, this);
48556             this.ds.un("datachanged", this.onDataChange, this);
48557             this.ds.un("add", this.onAdd, this);
48558             this.ds.un("remove", this.onRemove, this);
48559             this.ds.un("update", this.onUpdate, this);
48560             this.ds.un("clear", this.onClear, this);
48561         }
48562         if(ds){
48563             ds.on("load", this.onLoad, this);
48564             ds.on("datachanged", this.onDataChange, this);
48565             ds.on("add", this.onAdd, this);
48566             ds.on("remove", this.onRemove, this);
48567             ds.on("update", this.onUpdate, this);
48568             ds.on("clear", this.onClear, this);
48569         }
48570         this.ds = ds;
48571
48572         if(this.cm){
48573             this.cm.un("widthchange", this.onColWidthChange, this);
48574             this.cm.un("headerchange", this.onHeaderChange, this);
48575             this.cm.un("hiddenchange", this.onHiddenChange, this);
48576             this.cm.un("columnmoved", this.onColumnMove, this);
48577             this.cm.un("columnlockchange", this.onColumnLock, this);
48578         }
48579         if(cm){
48580             this.generateRules(cm);
48581             cm.on("widthchange", this.onColWidthChange, this);
48582             cm.on("headerchange", this.onHeaderChange, this);
48583             cm.on("hiddenchange", this.onHiddenChange, this);
48584             cm.on("columnmoved", this.onColumnMove, this);
48585             cm.on("columnlockchange", this.onColumnLock, this);
48586         }
48587         this.cm = cm;
48588     },
48589
48590     init: function(grid){
48591         Roo.grid.GridView.superclass.init.call(this, grid);
48592
48593         this.bind(grid.dataSource, grid.colModel);
48594
48595         grid.on("headerclick", this.handleHeaderClick, this);
48596
48597         if(grid.trackMouseOver){
48598             grid.on("mouseover", this.onRowOver, this);
48599             grid.on("mouseout", this.onRowOut, this);
48600         }
48601         grid.cancelTextSelection = function(){};
48602         this.gridId = grid.id;
48603
48604         var tpls = this.templates || {};
48605
48606         if(!tpls.master){
48607             tpls.master = new Roo.Template(
48608                '<div class="x-grid" hidefocus="true">',
48609                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48610                   '<div class="x-grid-topbar"></div>',
48611                   '<div class="x-grid-scroller"><div></div></div>',
48612                   '<div class="x-grid-locked">',
48613                       '<div class="x-grid-header">{lockedHeader}</div>',
48614                       '<div class="x-grid-body">{lockedBody}</div>',
48615                   "</div>",
48616                   '<div class="x-grid-viewport">',
48617                       '<div class="x-grid-header">{header}</div>',
48618                       '<div class="x-grid-body">{body}</div>',
48619                   "</div>",
48620                   '<div class="x-grid-bottombar"></div>',
48621                  
48622                   '<div class="x-grid-resize-proxy">&#160;</div>',
48623                "</div>"
48624             );
48625             tpls.master.disableformats = true;
48626         }
48627
48628         if(!tpls.header){
48629             tpls.header = new Roo.Template(
48630                '<table border="0" cellspacing="0" cellpadding="0">',
48631                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48632                "</table>{splits}"
48633             );
48634             tpls.header.disableformats = true;
48635         }
48636         tpls.header.compile();
48637
48638         if(!tpls.hcell){
48639             tpls.hcell = new Roo.Template(
48640                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48641                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48642                 "</div></td>"
48643              );
48644              tpls.hcell.disableFormats = true;
48645         }
48646         tpls.hcell.compile();
48647
48648         if(!tpls.hsplit){
48649             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48650             tpls.hsplit.disableFormats = true;
48651         }
48652         tpls.hsplit.compile();
48653
48654         if(!tpls.body){
48655             tpls.body = new Roo.Template(
48656                '<table border="0" cellspacing="0" cellpadding="0">',
48657                "<tbody>{rows}</tbody>",
48658                "</table>"
48659             );
48660             tpls.body.disableFormats = true;
48661         }
48662         tpls.body.compile();
48663
48664         if(!tpls.row){
48665             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48666             tpls.row.disableFormats = true;
48667         }
48668         tpls.row.compile();
48669
48670         if(!tpls.cell){
48671             tpls.cell = new Roo.Template(
48672                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48673                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48674                 "</td>"
48675             );
48676             tpls.cell.disableFormats = true;
48677         }
48678         tpls.cell.compile();
48679
48680         this.templates = tpls;
48681     },
48682
48683     // remap these for backwards compat
48684     onColWidthChange : function(){
48685         this.updateColumns.apply(this, arguments);
48686     },
48687     onHeaderChange : function(){
48688         this.updateHeaders.apply(this, arguments);
48689     }, 
48690     onHiddenChange : function(){
48691         this.handleHiddenChange.apply(this, arguments);
48692     },
48693     onColumnMove : function(){
48694         this.handleColumnMove.apply(this, arguments);
48695     },
48696     onColumnLock : function(){
48697         this.handleLockChange.apply(this, arguments);
48698     },
48699
48700     onDataChange : function(){
48701         this.refresh();
48702         this.updateHeaderSortState();
48703     },
48704
48705     onClear : function(){
48706         this.refresh();
48707     },
48708
48709     onUpdate : function(ds, record){
48710         this.refreshRow(record);
48711     },
48712
48713     refreshRow : function(record){
48714         var ds = this.ds, index;
48715         if(typeof record == 'number'){
48716             index = record;
48717             record = ds.getAt(index);
48718         }else{
48719             index = ds.indexOf(record);
48720         }
48721         this.insertRows(ds, index, index, true);
48722         this.onRemove(ds, record, index+1, true);
48723         this.syncRowHeights(index, index);
48724         this.layout();
48725         this.fireEvent("rowupdated", this, index, record);
48726     },
48727
48728     onAdd : function(ds, records, index){
48729         this.insertRows(ds, index, index + (records.length-1));
48730     },
48731
48732     onRemove : function(ds, record, index, isUpdate){
48733         if(isUpdate !== true){
48734             this.fireEvent("beforerowremoved", this, index, record);
48735         }
48736         var bt = this.getBodyTable(), lt = this.getLockedTable();
48737         if(bt.rows[index]){
48738             bt.firstChild.removeChild(bt.rows[index]);
48739         }
48740         if(lt.rows[index]){
48741             lt.firstChild.removeChild(lt.rows[index]);
48742         }
48743         if(isUpdate !== true){
48744             this.stripeRows(index);
48745             this.syncRowHeights(index, index);
48746             this.layout();
48747             this.fireEvent("rowremoved", this, index, record);
48748         }
48749     },
48750
48751     onLoad : function(){
48752         this.scrollToTop();
48753     },
48754
48755     /**
48756      * Scrolls the grid to the top
48757      */
48758     scrollToTop : function(){
48759         if(this.scroller){
48760             this.scroller.dom.scrollTop = 0;
48761             this.syncScroll();
48762         }
48763     },
48764
48765     /**
48766      * Gets a panel in the header of the grid that can be used for toolbars etc.
48767      * After modifying the contents of this panel a call to grid.autoSize() may be
48768      * required to register any changes in size.
48769      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48770      * @return Roo.Element
48771      */
48772     getHeaderPanel : function(doShow){
48773         if(doShow){
48774             this.headerPanel.show();
48775         }
48776         return this.headerPanel;
48777     },
48778
48779     /**
48780      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48781      * After modifying the contents of this panel a call to grid.autoSize() may be
48782      * required to register any changes in size.
48783      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48784      * @return Roo.Element
48785      */
48786     getFooterPanel : function(doShow){
48787         if(doShow){
48788             this.footerPanel.show();
48789         }
48790         return this.footerPanel;
48791     },
48792
48793     initElements : function(){
48794         var E = Roo.Element;
48795         var el = this.grid.getGridEl().dom.firstChild;
48796         var cs = el.childNodes;
48797
48798         this.el = new E(el);
48799         
48800          this.focusEl = new E(el.firstChild);
48801         this.focusEl.swallowEvent("click", true);
48802         
48803         this.headerPanel = new E(cs[1]);
48804         this.headerPanel.enableDisplayMode("block");
48805
48806         this.scroller = new E(cs[2]);
48807         this.scrollSizer = new E(this.scroller.dom.firstChild);
48808
48809         this.lockedWrap = new E(cs[3]);
48810         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48811         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48812
48813         this.mainWrap = new E(cs[4]);
48814         this.mainHd = new E(this.mainWrap.dom.firstChild);
48815         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48816
48817         this.footerPanel = new E(cs[5]);
48818         this.footerPanel.enableDisplayMode("block");
48819
48820         this.resizeProxy = new E(cs[6]);
48821
48822         this.headerSelector = String.format(
48823            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48824            this.lockedHd.id, this.mainHd.id
48825         );
48826
48827         this.splitterSelector = String.format(
48828            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48829            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48830         );
48831     },
48832     idToCssName : function(s)
48833     {
48834         return s.replace(/[^a-z0-9]+/ig, '-');
48835     },
48836
48837     getHeaderCell : function(index){
48838         return Roo.DomQuery.select(this.headerSelector)[index];
48839     },
48840
48841     getHeaderCellMeasure : function(index){
48842         return this.getHeaderCell(index).firstChild;
48843     },
48844
48845     getHeaderCellText : function(index){
48846         return this.getHeaderCell(index).firstChild.firstChild;
48847     },
48848
48849     getLockedTable : function(){
48850         return this.lockedBody.dom.firstChild;
48851     },
48852
48853     getBodyTable : function(){
48854         return this.mainBody.dom.firstChild;
48855     },
48856
48857     getLockedRow : function(index){
48858         return this.getLockedTable().rows[index];
48859     },
48860
48861     getRow : function(index){
48862         return this.getBodyTable().rows[index];
48863     },
48864
48865     getRowComposite : function(index){
48866         if(!this.rowEl){
48867             this.rowEl = new Roo.CompositeElementLite();
48868         }
48869         var els = [], lrow, mrow;
48870         if(lrow = this.getLockedRow(index)){
48871             els.push(lrow);
48872         }
48873         if(mrow = this.getRow(index)){
48874             els.push(mrow);
48875         }
48876         this.rowEl.elements = els;
48877         return this.rowEl;
48878     },
48879     /**
48880      * Gets the 'td' of the cell
48881      * 
48882      * @param {Integer} rowIndex row to select
48883      * @param {Integer} colIndex column to select
48884      * 
48885      * @return {Object} 
48886      */
48887     getCell : function(rowIndex, colIndex){
48888         var locked = this.cm.getLockedCount();
48889         var source;
48890         if(colIndex < locked){
48891             source = this.lockedBody.dom.firstChild;
48892         }else{
48893             source = this.mainBody.dom.firstChild;
48894             colIndex -= locked;
48895         }
48896         return source.rows[rowIndex].childNodes[colIndex];
48897     },
48898
48899     getCellText : function(rowIndex, colIndex){
48900         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48901     },
48902
48903     getCellBox : function(cell){
48904         var b = this.fly(cell).getBox();
48905         if(Roo.isOpera){ // opera fails to report the Y
48906             b.y = cell.offsetTop + this.mainBody.getY();
48907         }
48908         return b;
48909     },
48910
48911     getCellIndex : function(cell){
48912         var id = String(cell.className).match(this.cellRE);
48913         if(id){
48914             return parseInt(id[1], 10);
48915         }
48916         return 0;
48917     },
48918
48919     findHeaderIndex : function(n){
48920         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48921         return r ? this.getCellIndex(r) : false;
48922     },
48923
48924     findHeaderCell : function(n){
48925         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48926         return r ? r : false;
48927     },
48928
48929     findRowIndex : function(n){
48930         if(!n){
48931             return false;
48932         }
48933         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48934         return r ? r.rowIndex : false;
48935     },
48936
48937     findCellIndex : function(node){
48938         var stop = this.el.dom;
48939         while(node && node != stop){
48940             if(this.findRE.test(node.className)){
48941                 return this.getCellIndex(node);
48942             }
48943             node = node.parentNode;
48944         }
48945         return false;
48946     },
48947
48948     getColumnId : function(index){
48949         return this.cm.getColumnId(index);
48950     },
48951
48952     getSplitters : function()
48953     {
48954         if(this.splitterSelector){
48955            return Roo.DomQuery.select(this.splitterSelector);
48956         }else{
48957             return null;
48958       }
48959     },
48960
48961     getSplitter : function(index){
48962         return this.getSplitters()[index];
48963     },
48964
48965     onRowOver : function(e, t){
48966         var row;
48967         if((row = this.findRowIndex(t)) !== false){
48968             this.getRowComposite(row).addClass("x-grid-row-over");
48969         }
48970     },
48971
48972     onRowOut : function(e, t){
48973         var row;
48974         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48975             this.getRowComposite(row).removeClass("x-grid-row-over");
48976         }
48977     },
48978
48979     renderHeaders : function(){
48980         var cm = this.cm;
48981         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48982         var cb = [], lb = [], sb = [], lsb = [], p = {};
48983         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48984             p.cellId = "x-grid-hd-0-" + i;
48985             p.splitId = "x-grid-csplit-0-" + i;
48986             p.id = cm.getColumnId(i);
48987             p.title = cm.getColumnTooltip(i) || "";
48988             p.value = cm.getColumnHeader(i) || "";
48989             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48990             if(!cm.isLocked(i)){
48991                 cb[cb.length] = ct.apply(p);
48992                 sb[sb.length] = st.apply(p);
48993             }else{
48994                 lb[lb.length] = ct.apply(p);
48995                 lsb[lsb.length] = st.apply(p);
48996             }
48997         }
48998         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48999                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
49000     },
49001
49002     updateHeaders : function(){
49003         var html = this.renderHeaders();
49004         this.lockedHd.update(html[0]);
49005         this.mainHd.update(html[1]);
49006     },
49007
49008     /**
49009      * Focuses the specified row.
49010      * @param {Number} row The row index
49011      */
49012     focusRow : function(row)
49013     {
49014         //Roo.log('GridView.focusRow');
49015         var x = this.scroller.dom.scrollLeft;
49016         this.focusCell(row, 0, false);
49017         this.scroller.dom.scrollLeft = x;
49018     },
49019
49020     /**
49021      * Focuses the specified cell.
49022      * @param {Number} row The row index
49023      * @param {Number} col The column index
49024      * @param {Boolean} hscroll false to disable horizontal scrolling
49025      */
49026     focusCell : function(row, col, hscroll)
49027     {
49028         //Roo.log('GridView.focusCell');
49029         var el = this.ensureVisible(row, col, hscroll);
49030         this.focusEl.alignTo(el, "tl-tl");
49031         if(Roo.isGecko){
49032             this.focusEl.focus();
49033         }else{
49034             this.focusEl.focus.defer(1, this.focusEl);
49035         }
49036     },
49037
49038     /**
49039      * Scrolls the specified cell into view
49040      * @param {Number} row The row index
49041      * @param {Number} col The column index
49042      * @param {Boolean} hscroll false to disable horizontal scrolling
49043      */
49044     ensureVisible : function(row, col, hscroll)
49045     {
49046         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
49047         //return null; //disable for testing.
49048         if(typeof row != "number"){
49049             row = row.rowIndex;
49050         }
49051         if(row < 0 && row >= this.ds.getCount()){
49052             return  null;
49053         }
49054         col = (col !== undefined ? col : 0);
49055         var cm = this.grid.colModel;
49056         while(cm.isHidden(col)){
49057             col++;
49058         }
49059
49060         var el = this.getCell(row, col);
49061         if(!el){
49062             return null;
49063         }
49064         var c = this.scroller.dom;
49065
49066         var ctop = parseInt(el.offsetTop, 10);
49067         var cleft = parseInt(el.offsetLeft, 10);
49068         var cbot = ctop + el.offsetHeight;
49069         var cright = cleft + el.offsetWidth;
49070         
49071         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
49072         var stop = parseInt(c.scrollTop, 10);
49073         var sleft = parseInt(c.scrollLeft, 10);
49074         var sbot = stop + ch;
49075         var sright = sleft + c.clientWidth;
49076         /*
49077         Roo.log('GridView.ensureVisible:' +
49078                 ' ctop:' + ctop +
49079                 ' c.clientHeight:' + c.clientHeight +
49080                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
49081                 ' stop:' + stop +
49082                 ' cbot:' + cbot +
49083                 ' sbot:' + sbot +
49084                 ' ch:' + ch  
49085                 );
49086         */
49087         if(ctop < stop){
49088              c.scrollTop = ctop;
49089             //Roo.log("set scrolltop to ctop DISABLE?");
49090         }else if(cbot > sbot){
49091             //Roo.log("set scrolltop to cbot-ch");
49092             c.scrollTop = cbot-ch;
49093         }
49094         
49095         if(hscroll !== false){
49096             if(cleft < sleft){
49097                 c.scrollLeft = cleft;
49098             }else if(cright > sright){
49099                 c.scrollLeft = cright-c.clientWidth;
49100             }
49101         }
49102          
49103         return el;
49104     },
49105
49106     updateColumns : function(){
49107         this.grid.stopEditing();
49108         var cm = this.grid.colModel, colIds = this.getColumnIds();
49109         //var totalWidth = cm.getTotalWidth();
49110         var pos = 0;
49111         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49112             //if(cm.isHidden(i)) continue;
49113             var w = cm.getColumnWidth(i);
49114             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49115             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49116         }
49117         this.updateSplitters();
49118     },
49119
49120     generateRules : function(cm){
49121         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
49122         Roo.util.CSS.removeStyleSheet(rulesId);
49123         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49124             var cid = cm.getColumnId(i);
49125             var align = '';
49126             if(cm.config[i].align){
49127                 align = 'text-align:'+cm.config[i].align+';';
49128             }
49129             var hidden = '';
49130             if(cm.isHidden(i)){
49131                 hidden = 'display:none;';
49132             }
49133             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49134             ruleBuf.push(
49135                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49136                     this.hdSelector, cid, " {\n", align, width, "}\n",
49137                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49138                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49139         }
49140         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49141     },
49142
49143     updateSplitters : function(){
49144         var cm = this.cm, s = this.getSplitters();
49145         if(s){ // splitters not created yet
49146             var pos = 0, locked = true;
49147             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49148                 if(cm.isHidden(i)) continue;
49149                 var w = cm.getColumnWidth(i); // make sure it's a number
49150                 if(!cm.isLocked(i) && locked){
49151                     pos = 0;
49152                     locked = false;
49153                 }
49154                 pos += w;
49155                 s[i].style.left = (pos-this.splitOffset) + "px";
49156             }
49157         }
49158     },
49159
49160     handleHiddenChange : function(colModel, colIndex, hidden){
49161         if(hidden){
49162             this.hideColumn(colIndex);
49163         }else{
49164             this.unhideColumn(colIndex);
49165         }
49166     },
49167
49168     hideColumn : function(colIndex){
49169         var cid = this.getColumnId(colIndex);
49170         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49171         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49172         if(Roo.isSafari){
49173             this.updateHeaders();
49174         }
49175         this.updateSplitters();
49176         this.layout();
49177     },
49178
49179     unhideColumn : function(colIndex){
49180         var cid = this.getColumnId(colIndex);
49181         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49182         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49183
49184         if(Roo.isSafari){
49185             this.updateHeaders();
49186         }
49187         this.updateSplitters();
49188         this.layout();
49189     },
49190
49191     insertRows : function(dm, firstRow, lastRow, isUpdate){
49192         if(firstRow == 0 && lastRow == dm.getCount()-1){
49193             this.refresh();
49194         }else{
49195             if(!isUpdate){
49196                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49197             }
49198             var s = this.getScrollState();
49199             var markup = this.renderRows(firstRow, lastRow);
49200             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49201             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49202             this.restoreScroll(s);
49203             if(!isUpdate){
49204                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49205                 this.syncRowHeights(firstRow, lastRow);
49206                 this.stripeRows(firstRow);
49207                 this.layout();
49208             }
49209         }
49210     },
49211
49212     bufferRows : function(markup, target, index){
49213         var before = null, trows = target.rows, tbody = target.tBodies[0];
49214         if(index < trows.length){
49215             before = trows[index];
49216         }
49217         var b = document.createElement("div");
49218         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49219         var rows = b.firstChild.rows;
49220         for(var i = 0, len = rows.length; i < len; i++){
49221             if(before){
49222                 tbody.insertBefore(rows[0], before);
49223             }else{
49224                 tbody.appendChild(rows[0]);
49225             }
49226         }
49227         b.innerHTML = "";
49228         b = null;
49229     },
49230
49231     deleteRows : function(dm, firstRow, lastRow){
49232         if(dm.getRowCount()<1){
49233             this.fireEvent("beforerefresh", this);
49234             this.mainBody.update("");
49235             this.lockedBody.update("");
49236             this.fireEvent("refresh", this);
49237         }else{
49238             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49239             var bt = this.getBodyTable();
49240             var tbody = bt.firstChild;
49241             var rows = bt.rows;
49242             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49243                 tbody.removeChild(rows[firstRow]);
49244             }
49245             this.stripeRows(firstRow);
49246             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49247         }
49248     },
49249
49250     updateRows : function(dataSource, firstRow, lastRow){
49251         var s = this.getScrollState();
49252         this.refresh();
49253         this.restoreScroll(s);
49254     },
49255
49256     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49257         if(!noRefresh){
49258            this.refresh();
49259         }
49260         this.updateHeaderSortState();
49261     },
49262
49263     getScrollState : function(){
49264         
49265         var sb = this.scroller.dom;
49266         return {left: sb.scrollLeft, top: sb.scrollTop};
49267     },
49268
49269     stripeRows : function(startRow){
49270         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49271             return;
49272         }
49273         startRow = startRow || 0;
49274         var rows = this.getBodyTable().rows;
49275         var lrows = this.getLockedTable().rows;
49276         var cls = ' x-grid-row-alt ';
49277         for(var i = startRow, len = rows.length; i < len; i++){
49278             var row = rows[i], lrow = lrows[i];
49279             var isAlt = ((i+1) % 2 == 0);
49280             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49281             if(isAlt == hasAlt){
49282                 continue;
49283             }
49284             if(isAlt){
49285                 row.className += " x-grid-row-alt";
49286             }else{
49287                 row.className = row.className.replace("x-grid-row-alt", "");
49288             }
49289             if(lrow){
49290                 lrow.className = row.className;
49291             }
49292         }
49293     },
49294
49295     restoreScroll : function(state){
49296         //Roo.log('GridView.restoreScroll');
49297         var sb = this.scroller.dom;
49298         sb.scrollLeft = state.left;
49299         sb.scrollTop = state.top;
49300         this.syncScroll();
49301     },
49302
49303     syncScroll : function(){
49304         //Roo.log('GridView.syncScroll');
49305         var sb = this.scroller.dom;
49306         var sh = this.mainHd.dom;
49307         var bs = this.mainBody.dom;
49308         var lv = this.lockedBody.dom;
49309         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49310         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49311     },
49312
49313     handleScroll : function(e){
49314         this.syncScroll();
49315         var sb = this.scroller.dom;
49316         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49317         e.stopEvent();
49318     },
49319
49320     handleWheel : function(e){
49321         var d = e.getWheelDelta();
49322         this.scroller.dom.scrollTop -= d*22;
49323         // set this here to prevent jumpy scrolling on large tables
49324         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49325         e.stopEvent();
49326     },
49327
49328     renderRows : function(startRow, endRow){
49329         // pull in all the crap needed to render rows
49330         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49331         var colCount = cm.getColumnCount();
49332
49333         if(ds.getCount() < 1){
49334             return ["", ""];
49335         }
49336
49337         // build a map for all the columns
49338         var cs = [];
49339         for(var i = 0; i < colCount; i++){
49340             var name = cm.getDataIndex(i);
49341             cs[i] = {
49342                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49343                 renderer : cm.getRenderer(i),
49344                 id : cm.getColumnId(i),
49345                 locked : cm.isLocked(i)
49346             };
49347         }
49348
49349         startRow = startRow || 0;
49350         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49351
49352         // records to render
49353         var rs = ds.getRange(startRow, endRow);
49354
49355         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49356     },
49357
49358     // As much as I hate to duplicate code, this was branched because FireFox really hates
49359     // [].join("") on strings. The performance difference was substantial enough to
49360     // branch this function
49361     doRender : Roo.isGecko ?
49362             function(cs, rs, ds, startRow, colCount, stripe){
49363                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49364                 // buffers
49365                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49366                 
49367                 var hasListener = this.grid.hasListener('rowclass');
49368                 var rowcfg = {};
49369                 for(var j = 0, len = rs.length; j < len; j++){
49370                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49371                     for(var i = 0; i < colCount; i++){
49372                         c = cs[i];
49373                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49374                         p.id = c.id;
49375                         p.css = p.attr = "";
49376                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49377                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49378                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49379                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49380                         }
49381                         var markup = ct.apply(p);
49382                         if(!c.locked){
49383                             cb+= markup;
49384                         }else{
49385                             lcb+= markup;
49386                         }
49387                     }
49388                     var alt = [];
49389                     if(stripe && ((rowIndex+1) % 2 == 0)){
49390                         alt.push("x-grid-row-alt")
49391                     }
49392                     if(r.dirty){
49393                         alt.push(  " x-grid-dirty-row");
49394                     }
49395                     rp.cells = lcb;
49396                     if(this.getRowClass){
49397                         alt.push(this.getRowClass(r, rowIndex));
49398                     }
49399                     if (hasListener) {
49400                         rowcfg = {
49401                              
49402                             record: r,
49403                             rowIndex : rowIndex,
49404                             rowClass : ''
49405                         }
49406                         this.grid.fireEvent('rowclass', this, rowcfg);
49407                         alt.push(rowcfg.rowClass);
49408                     }
49409                     rp.alt = alt.join(" ");
49410                     lbuf+= rt.apply(rp);
49411                     rp.cells = cb;
49412                     buf+=  rt.apply(rp);
49413                 }
49414                 return [lbuf, buf];
49415             } :
49416             function(cs, rs, ds, startRow, colCount, stripe){
49417                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49418                 // buffers
49419                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49420                 var hasListener = this.grid.hasListener('rowclass');
49421  
49422                 var rowcfg = {};
49423                 for(var j = 0, len = rs.length; j < len; j++){
49424                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49425                     for(var i = 0; i < colCount; i++){
49426                         c = cs[i];
49427                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49428                         p.id = c.id;
49429                         p.css = p.attr = "";
49430                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49431                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49432                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49433                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49434                         }
49435                         
49436                         var markup = ct.apply(p);
49437                         if(!c.locked){
49438                             cb[cb.length] = markup;
49439                         }else{
49440                             lcb[lcb.length] = markup;
49441                         }
49442                     }
49443                     var alt = [];
49444                     if(stripe && ((rowIndex+1) % 2 == 0)){
49445                         alt.push( "x-grid-row-alt");
49446                     }
49447                     if(r.dirty){
49448                         alt.push(" x-grid-dirty-row");
49449                     }
49450                     rp.cells = lcb;
49451                     if(this.getRowClass){
49452                         alt.push( this.getRowClass(r, rowIndex));
49453                     }
49454                     if (hasListener) {
49455                         rowcfg = {
49456                              
49457                             record: r,
49458                             rowIndex : rowIndex,
49459                             rowClass : ''
49460                         }
49461                         this.grid.fireEvent('rowclass', this, rowcfg);
49462                         alt.push(rowcfg.rowClass);
49463                     }
49464                     rp.alt = alt.join(" ");
49465                     rp.cells = lcb.join("");
49466                     lbuf[lbuf.length] = rt.apply(rp);
49467                     rp.cells = cb.join("");
49468                     buf[buf.length] =  rt.apply(rp);
49469                 }
49470                 return [lbuf.join(""), buf.join("")];
49471             },
49472
49473     renderBody : function(){
49474         var markup = this.renderRows();
49475         var bt = this.templates.body;
49476         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49477     },
49478
49479     /**
49480      * Refreshes the grid
49481      * @param {Boolean} headersToo
49482      */
49483     refresh : function(headersToo){
49484         this.fireEvent("beforerefresh", this);
49485         this.grid.stopEditing();
49486         var result = this.renderBody();
49487         this.lockedBody.update(result[0]);
49488         this.mainBody.update(result[1]);
49489         if(headersToo === true){
49490             this.updateHeaders();
49491             this.updateColumns();
49492             this.updateSplitters();
49493             this.updateHeaderSortState();
49494         }
49495         this.syncRowHeights();
49496         this.layout();
49497         this.fireEvent("refresh", this);
49498     },
49499
49500     handleColumnMove : function(cm, oldIndex, newIndex){
49501         this.indexMap = null;
49502         var s = this.getScrollState();
49503         this.refresh(true);
49504         this.restoreScroll(s);
49505         this.afterMove(newIndex);
49506     },
49507
49508     afterMove : function(colIndex){
49509         if(this.enableMoveAnim && Roo.enableFx){
49510             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49511         }
49512         // if multisort - fix sortOrder, and reload..
49513         if (this.grid.dataSource.multiSort) {
49514             // the we can call sort again..
49515             var dm = this.grid.dataSource;
49516             var cm = this.grid.colModel;
49517             var so = [];
49518             for(var i = 0; i < cm.config.length; i++ ) {
49519                 
49520                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49521                     continue; // dont' bother, it's not in sort list or being set.
49522                 }
49523                 
49524                 so.push(cm.config[i].dataIndex);
49525             };
49526             dm.sortOrder = so;
49527             dm.load(dm.lastOptions);
49528             
49529             
49530         }
49531         
49532     },
49533
49534     updateCell : function(dm, rowIndex, dataIndex){
49535         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49536         if(typeof colIndex == "undefined"){ // not present in grid
49537             return;
49538         }
49539         var cm = this.grid.colModel;
49540         var cell = this.getCell(rowIndex, colIndex);
49541         var cellText = this.getCellText(rowIndex, colIndex);
49542
49543         var p = {
49544             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49545             id : cm.getColumnId(colIndex),
49546             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49547         };
49548         var renderer = cm.getRenderer(colIndex);
49549         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49550         if(typeof val == "undefined" || val === "") val = "&#160;";
49551         cellText.innerHTML = val;
49552         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49553         this.syncRowHeights(rowIndex, rowIndex);
49554     },
49555
49556     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49557         var maxWidth = 0;
49558         if(this.grid.autoSizeHeaders){
49559             var h = this.getHeaderCellMeasure(colIndex);
49560             maxWidth = Math.max(maxWidth, h.scrollWidth);
49561         }
49562         var tb, index;
49563         if(this.cm.isLocked(colIndex)){
49564             tb = this.getLockedTable();
49565             index = colIndex;
49566         }else{
49567             tb = this.getBodyTable();
49568             index = colIndex - this.cm.getLockedCount();
49569         }
49570         if(tb && tb.rows){
49571             var rows = tb.rows;
49572             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49573             for(var i = 0; i < stopIndex; i++){
49574                 var cell = rows[i].childNodes[index].firstChild;
49575                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49576             }
49577         }
49578         return maxWidth + /*margin for error in IE*/ 5;
49579     },
49580     /**
49581      * Autofit a column to its content.
49582      * @param {Number} colIndex
49583      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49584      */
49585      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49586          if(this.cm.isHidden(colIndex)){
49587              return; // can't calc a hidden column
49588          }
49589         if(forceMinSize){
49590             var cid = this.cm.getColumnId(colIndex);
49591             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49592            if(this.grid.autoSizeHeaders){
49593                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49594            }
49595         }
49596         var newWidth = this.calcColumnWidth(colIndex);
49597         this.cm.setColumnWidth(colIndex,
49598             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49599         if(!suppressEvent){
49600             this.grid.fireEvent("columnresize", colIndex, newWidth);
49601         }
49602     },
49603
49604     /**
49605      * Autofits all columns to their content and then expands to fit any extra space in the grid
49606      */
49607      autoSizeColumns : function(){
49608         var cm = this.grid.colModel;
49609         var colCount = cm.getColumnCount();
49610         for(var i = 0; i < colCount; i++){
49611             this.autoSizeColumn(i, true, true);
49612         }
49613         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49614             this.fitColumns();
49615         }else{
49616             this.updateColumns();
49617             this.layout();
49618         }
49619     },
49620
49621     /**
49622      * Autofits all columns to the grid's width proportionate with their current size
49623      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49624      */
49625     fitColumns : function(reserveScrollSpace){
49626         var cm = this.grid.colModel;
49627         var colCount = cm.getColumnCount();
49628         var cols = [];
49629         var width = 0;
49630         var i, w;
49631         for (i = 0; i < colCount; i++){
49632             if(!cm.isHidden(i) && !cm.isFixed(i)){
49633                 w = cm.getColumnWidth(i);
49634                 cols.push(i);
49635                 cols.push(w);
49636                 width += w;
49637             }
49638         }
49639         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49640         if(reserveScrollSpace){
49641             avail -= 17;
49642         }
49643         var frac = (avail - cm.getTotalWidth())/width;
49644         while (cols.length){
49645             w = cols.pop();
49646             i = cols.pop();
49647             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49648         }
49649         this.updateColumns();
49650         this.layout();
49651     },
49652
49653     onRowSelect : function(rowIndex){
49654         var row = this.getRowComposite(rowIndex);
49655         row.addClass("x-grid-row-selected");
49656     },
49657
49658     onRowDeselect : function(rowIndex){
49659         var row = this.getRowComposite(rowIndex);
49660         row.removeClass("x-grid-row-selected");
49661     },
49662
49663     onCellSelect : function(row, col){
49664         var cell = this.getCell(row, col);
49665         if(cell){
49666             Roo.fly(cell).addClass("x-grid-cell-selected");
49667         }
49668     },
49669
49670     onCellDeselect : function(row, col){
49671         var cell = this.getCell(row, col);
49672         if(cell){
49673             Roo.fly(cell).removeClass("x-grid-cell-selected");
49674         }
49675     },
49676
49677     updateHeaderSortState : function(){
49678         
49679         // sort state can be single { field: xxx, direction : yyy}
49680         // or   { xxx=>ASC , yyy : DESC ..... }
49681         
49682         var mstate = {};
49683         if (!this.ds.multiSort) { 
49684             var state = this.ds.getSortState();
49685             if(!state){
49686                 return;
49687             }
49688             mstate[state.field] = state.direction;
49689             // FIXME... - this is not used here.. but might be elsewhere..
49690             this.sortState = state;
49691             
49692         } else {
49693             mstate = this.ds.sortToggle;
49694         }
49695         //remove existing sort classes..
49696         
49697         var sc = this.sortClasses;
49698         var hds = this.el.select(this.headerSelector).removeClass(sc);
49699         
49700         for(var f in mstate) {
49701         
49702             var sortColumn = this.cm.findColumnIndex(f);
49703             
49704             if(sortColumn != -1){
49705                 var sortDir = mstate[f];        
49706                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49707             }
49708         }
49709         
49710          
49711         
49712     },
49713
49714
49715     handleHeaderClick : function(g, index){
49716         if(this.headersDisabled){
49717             return;
49718         }
49719         var dm = g.dataSource, cm = g.colModel;
49720         if(!cm.isSortable(index)){
49721             return;
49722         }
49723         g.stopEditing();
49724         
49725         if (dm.multiSort) {
49726             // update the sortOrder
49727             var so = [];
49728             for(var i = 0; i < cm.config.length; i++ ) {
49729                 
49730                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49731                     continue; // dont' bother, it's not in sort list or being set.
49732                 }
49733                 
49734                 so.push(cm.config[i].dataIndex);
49735             };
49736             dm.sortOrder = so;
49737         }
49738         
49739         
49740         dm.sort(cm.getDataIndex(index));
49741     },
49742
49743
49744     destroy : function(){
49745         if(this.colMenu){
49746             this.colMenu.removeAll();
49747             Roo.menu.MenuMgr.unregister(this.colMenu);
49748             this.colMenu.getEl().remove();
49749             delete this.colMenu;
49750         }
49751         if(this.hmenu){
49752             this.hmenu.removeAll();
49753             Roo.menu.MenuMgr.unregister(this.hmenu);
49754             this.hmenu.getEl().remove();
49755             delete this.hmenu;
49756         }
49757         if(this.grid.enableColumnMove){
49758             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49759             if(dds){
49760                 for(var dd in dds){
49761                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49762                         var elid = dds[dd].dragElId;
49763                         dds[dd].unreg();
49764                         Roo.get(elid).remove();
49765                     } else if(dds[dd].config.isTarget){
49766                         dds[dd].proxyTop.remove();
49767                         dds[dd].proxyBottom.remove();
49768                         dds[dd].unreg();
49769                     }
49770                     if(Roo.dd.DDM.locationCache[dd]){
49771                         delete Roo.dd.DDM.locationCache[dd];
49772                     }
49773                 }
49774                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49775             }
49776         }
49777         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49778         this.bind(null, null);
49779         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49780     },
49781
49782     handleLockChange : function(){
49783         this.refresh(true);
49784     },
49785
49786     onDenyColumnLock : function(){
49787
49788     },
49789
49790     onDenyColumnHide : function(){
49791
49792     },
49793
49794     handleHdMenuClick : function(item){
49795         var index = this.hdCtxIndex;
49796         var cm = this.cm, ds = this.ds;
49797         switch(item.id){
49798             case "asc":
49799                 ds.sort(cm.getDataIndex(index), "ASC");
49800                 break;
49801             case "desc":
49802                 ds.sort(cm.getDataIndex(index), "DESC");
49803                 break;
49804             case "lock":
49805                 var lc = cm.getLockedCount();
49806                 if(cm.getColumnCount(true) <= lc+1){
49807                     this.onDenyColumnLock();
49808                     return;
49809                 }
49810                 if(lc != index){
49811                     cm.setLocked(index, true, true);
49812                     cm.moveColumn(index, lc);
49813                     this.grid.fireEvent("columnmove", index, lc);
49814                 }else{
49815                     cm.setLocked(index, true);
49816                 }
49817             break;
49818             case "unlock":
49819                 var lc = cm.getLockedCount();
49820                 if((lc-1) != index){
49821                     cm.setLocked(index, false, true);
49822                     cm.moveColumn(index, lc-1);
49823                     this.grid.fireEvent("columnmove", index, lc-1);
49824                 }else{
49825                     cm.setLocked(index, false);
49826                 }
49827             break;
49828             default:
49829                 index = cm.getIndexById(item.id.substr(4));
49830                 if(index != -1){
49831                     if(item.checked && cm.getColumnCount(true) <= 1){
49832                         this.onDenyColumnHide();
49833                         return false;
49834                     }
49835                     cm.setHidden(index, item.checked);
49836                 }
49837         }
49838         return true;
49839     },
49840
49841     beforeColMenuShow : function(){
49842         var cm = this.cm,  colCount = cm.getColumnCount();
49843         this.colMenu.removeAll();
49844         for(var i = 0; i < colCount; i++){
49845             this.colMenu.add(new Roo.menu.CheckItem({
49846                 id: "col-"+cm.getColumnId(i),
49847                 text: cm.getColumnHeader(i),
49848                 checked: !cm.isHidden(i),
49849                 hideOnClick:false
49850             }));
49851         }
49852     },
49853
49854     handleHdCtx : function(g, index, e){
49855         e.stopEvent();
49856         var hd = this.getHeaderCell(index);
49857         this.hdCtxIndex = index;
49858         var ms = this.hmenu.items, cm = this.cm;
49859         ms.get("asc").setDisabled(!cm.isSortable(index));
49860         ms.get("desc").setDisabled(!cm.isSortable(index));
49861         if(this.grid.enableColLock !== false){
49862             ms.get("lock").setDisabled(cm.isLocked(index));
49863             ms.get("unlock").setDisabled(!cm.isLocked(index));
49864         }
49865         this.hmenu.show(hd, "tl-bl");
49866     },
49867
49868     handleHdOver : function(e){
49869         var hd = this.findHeaderCell(e.getTarget());
49870         if(hd && !this.headersDisabled){
49871             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49872                this.fly(hd).addClass("x-grid-hd-over");
49873             }
49874         }
49875     },
49876
49877     handleHdOut : function(e){
49878         var hd = this.findHeaderCell(e.getTarget());
49879         if(hd){
49880             this.fly(hd).removeClass("x-grid-hd-over");
49881         }
49882     },
49883
49884     handleSplitDblClick : function(e, t){
49885         var i = this.getCellIndex(t);
49886         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49887             this.autoSizeColumn(i, true);
49888             this.layout();
49889         }
49890     },
49891
49892     render : function(){
49893
49894         var cm = this.cm;
49895         var colCount = cm.getColumnCount();
49896
49897         if(this.grid.monitorWindowResize === true){
49898             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49899         }
49900         var header = this.renderHeaders();
49901         var body = this.templates.body.apply({rows:""});
49902         var html = this.templates.master.apply({
49903             lockedBody: body,
49904             body: body,
49905             lockedHeader: header[0],
49906             header: header[1]
49907         });
49908
49909         //this.updateColumns();
49910
49911         this.grid.getGridEl().dom.innerHTML = html;
49912
49913         this.initElements();
49914         
49915         // a kludge to fix the random scolling effect in webkit
49916         this.el.on("scroll", function() {
49917             this.el.dom.scrollTop=0; // hopefully not recursive..
49918         },this);
49919
49920         this.scroller.on("scroll", this.handleScroll, this);
49921         this.lockedBody.on("mousewheel", this.handleWheel, this);
49922         this.mainBody.on("mousewheel", this.handleWheel, this);
49923
49924         this.mainHd.on("mouseover", this.handleHdOver, this);
49925         this.mainHd.on("mouseout", this.handleHdOut, this);
49926         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49927                 {delegate: "."+this.splitClass});
49928
49929         this.lockedHd.on("mouseover", this.handleHdOver, this);
49930         this.lockedHd.on("mouseout", this.handleHdOut, this);
49931         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49932                 {delegate: "."+this.splitClass});
49933
49934         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49935             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49936         }
49937
49938         this.updateSplitters();
49939
49940         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49941             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49942             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49943         }
49944
49945         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49946             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49947             this.hmenu.add(
49948                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49949                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49950             );
49951             if(this.grid.enableColLock !== false){
49952                 this.hmenu.add('-',
49953                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49954                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49955                 );
49956             }
49957             if(this.grid.enableColumnHide !== false){
49958
49959                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49960                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49961                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49962
49963                 this.hmenu.add('-',
49964                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49965                 );
49966             }
49967             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49968
49969             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49970         }
49971
49972         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49973             this.dd = new Roo.grid.GridDragZone(this.grid, {
49974                 ddGroup : this.grid.ddGroup || 'GridDD'
49975             });
49976         }
49977
49978         /*
49979         for(var i = 0; i < colCount; i++){
49980             if(cm.isHidden(i)){
49981                 this.hideColumn(i);
49982             }
49983             if(cm.config[i].align){
49984                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49985                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49986             }
49987         }*/
49988         
49989         this.updateHeaderSortState();
49990
49991         this.beforeInitialResize();
49992         this.layout(true);
49993
49994         // two part rendering gives faster view to the user
49995         this.renderPhase2.defer(1, this);
49996     },
49997
49998     renderPhase2 : function(){
49999         // render the rows now
50000         this.refresh();
50001         if(this.grid.autoSizeColumns){
50002             this.autoSizeColumns();
50003         }
50004     },
50005
50006     beforeInitialResize : function(){
50007
50008     },
50009
50010     onColumnSplitterMoved : function(i, w){
50011         this.userResized = true;
50012         var cm = this.grid.colModel;
50013         cm.setColumnWidth(i, w, true);
50014         var cid = cm.getColumnId(i);
50015         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50016         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50017         this.updateSplitters();
50018         this.layout();
50019         this.grid.fireEvent("columnresize", i, w);
50020     },
50021
50022     syncRowHeights : function(startIndex, endIndex){
50023         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
50024             startIndex = startIndex || 0;
50025             var mrows = this.getBodyTable().rows;
50026             var lrows = this.getLockedTable().rows;
50027             var len = mrows.length-1;
50028             endIndex = Math.min(endIndex || len, len);
50029             for(var i = startIndex; i <= endIndex; i++){
50030                 var m = mrows[i], l = lrows[i];
50031                 var h = Math.max(m.offsetHeight, l.offsetHeight);
50032                 m.style.height = l.style.height = h + "px";
50033             }
50034         }
50035     },
50036
50037     layout : function(initialRender, is2ndPass){
50038         var g = this.grid;
50039         var auto = g.autoHeight;
50040         var scrollOffset = 16;
50041         var c = g.getGridEl(), cm = this.cm,
50042                 expandCol = g.autoExpandColumn,
50043                 gv = this;
50044         //c.beginMeasure();
50045
50046         if(!c.dom.offsetWidth){ // display:none?
50047             if(initialRender){
50048                 this.lockedWrap.show();
50049                 this.mainWrap.show();
50050             }
50051             return;
50052         }
50053
50054         var hasLock = this.cm.isLocked(0);
50055
50056         var tbh = this.headerPanel.getHeight();
50057         var bbh = this.footerPanel.getHeight();
50058
50059         if(auto){
50060             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
50061             var newHeight = ch + c.getBorderWidth("tb");
50062             if(g.maxHeight){
50063                 newHeight = Math.min(g.maxHeight, newHeight);
50064             }
50065             c.setHeight(newHeight);
50066         }
50067
50068         if(g.autoWidth){
50069             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
50070         }
50071
50072         var s = this.scroller;
50073
50074         var csize = c.getSize(true);
50075
50076         this.el.setSize(csize.width, csize.height);
50077
50078         this.headerPanel.setWidth(csize.width);
50079         this.footerPanel.setWidth(csize.width);
50080
50081         var hdHeight = this.mainHd.getHeight();
50082         var vw = csize.width;
50083         var vh = csize.height - (tbh + bbh);
50084
50085         s.setSize(vw, vh);
50086
50087         var bt = this.getBodyTable();
50088         var ltWidth = hasLock ?
50089                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
50090
50091         var scrollHeight = bt.offsetHeight;
50092         var scrollWidth = ltWidth + bt.offsetWidth;
50093         var vscroll = false, hscroll = false;
50094
50095         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
50096
50097         var lw = this.lockedWrap, mw = this.mainWrap;
50098         var lb = this.lockedBody, mb = this.mainBody;
50099
50100         setTimeout(function(){
50101             var t = s.dom.offsetTop;
50102             var w = s.dom.clientWidth,
50103                 h = s.dom.clientHeight;
50104
50105             lw.setTop(t);
50106             lw.setSize(ltWidth, h);
50107
50108             mw.setLeftTop(ltWidth, t);
50109             mw.setSize(w-ltWidth, h);
50110
50111             lb.setHeight(h-hdHeight);
50112             mb.setHeight(h-hdHeight);
50113
50114             if(is2ndPass !== true && !gv.userResized && expandCol){
50115                 // high speed resize without full column calculation
50116                 
50117                 var ci = cm.getIndexById(expandCol);
50118                 if (ci < 0) {
50119                     ci = cm.findColumnIndex(expandCol);
50120                 }
50121                 ci = Math.max(0, ci); // make sure it's got at least the first col.
50122                 var expandId = cm.getColumnId(ci);
50123                 var  tw = cm.getTotalWidth(false);
50124                 var currentWidth = cm.getColumnWidth(ci);
50125                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
50126                 if(currentWidth != cw){
50127                     cm.setColumnWidth(ci, cw, true);
50128                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50129                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50130                     gv.updateSplitters();
50131                     gv.layout(false, true);
50132                 }
50133             }
50134
50135             if(initialRender){
50136                 lw.show();
50137                 mw.show();
50138             }
50139             //c.endMeasure();
50140         }, 10);
50141     },
50142
50143     onWindowResize : function(){
50144         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50145             return;
50146         }
50147         this.layout();
50148     },
50149
50150     appendFooter : function(parentEl){
50151         return null;
50152     },
50153
50154     sortAscText : "Sort Ascending",
50155     sortDescText : "Sort Descending",
50156     lockText : "Lock Column",
50157     unlockText : "Unlock Column",
50158     columnsText : "Columns"
50159 });
50160
50161
50162 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50163     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50164     this.proxy.el.addClass('x-grid3-col-dd');
50165 };
50166
50167 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50168     handleMouseDown : function(e){
50169
50170     },
50171
50172     callHandleMouseDown : function(e){
50173         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50174     }
50175 });
50176 /*
50177  * Based on:
50178  * Ext JS Library 1.1.1
50179  * Copyright(c) 2006-2007, Ext JS, LLC.
50180  *
50181  * Originally Released Under LGPL - original licence link has changed is not relivant.
50182  *
50183  * Fork - LGPL
50184  * <script type="text/javascript">
50185  */
50186  
50187 // private
50188 // This is a support class used internally by the Grid components
50189 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50190     this.grid = grid;
50191     this.view = grid.getView();
50192     this.proxy = this.view.resizeProxy;
50193     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50194         "gridSplitters" + this.grid.getGridEl().id, {
50195         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50196     });
50197     this.setHandleElId(Roo.id(hd));
50198     this.setOuterHandleElId(Roo.id(hd2));
50199     this.scroll = false;
50200 };
50201 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50202     fly: Roo.Element.fly,
50203
50204     b4StartDrag : function(x, y){
50205         this.view.headersDisabled = true;
50206         this.proxy.setHeight(this.view.mainWrap.getHeight());
50207         var w = this.cm.getColumnWidth(this.cellIndex);
50208         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50209         this.resetConstraints();
50210         this.setXConstraint(minw, 1000);
50211         this.setYConstraint(0, 0);
50212         this.minX = x - minw;
50213         this.maxX = x + 1000;
50214         this.startPos = x;
50215         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50216     },
50217
50218
50219     handleMouseDown : function(e){
50220         ev = Roo.EventObject.setEvent(e);
50221         var t = this.fly(ev.getTarget());
50222         if(t.hasClass("x-grid-split")){
50223             this.cellIndex = this.view.getCellIndex(t.dom);
50224             this.split = t.dom;
50225             this.cm = this.grid.colModel;
50226             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50227                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50228             }
50229         }
50230     },
50231
50232     endDrag : function(e){
50233         this.view.headersDisabled = false;
50234         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50235         var diff = endX - this.startPos;
50236         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50237     },
50238
50239     autoOffset : function(){
50240         this.setDelta(0,0);
50241     }
50242 });/*
50243  * Based on:
50244  * Ext JS Library 1.1.1
50245  * Copyright(c) 2006-2007, Ext JS, LLC.
50246  *
50247  * Originally Released Under LGPL - original licence link has changed is not relivant.
50248  *
50249  * Fork - LGPL
50250  * <script type="text/javascript">
50251  */
50252  
50253 // private
50254 // This is a support class used internally by the Grid components
50255 Roo.grid.GridDragZone = function(grid, config){
50256     this.view = grid.getView();
50257     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50258     if(this.view.lockedBody){
50259         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50260         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50261     }
50262     this.scroll = false;
50263     this.grid = grid;
50264     this.ddel = document.createElement('div');
50265     this.ddel.className = 'x-grid-dd-wrap';
50266 };
50267
50268 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50269     ddGroup : "GridDD",
50270
50271     getDragData : function(e){
50272         var t = Roo.lib.Event.getTarget(e);
50273         var rowIndex = this.view.findRowIndex(t);
50274         if(rowIndex !== false){
50275             var sm = this.grid.selModel;
50276             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50277               //  sm.mouseDown(e, t);
50278             //}
50279             if (e.hasModifier()){
50280                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50281             }
50282             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50283         }
50284         return false;
50285     },
50286
50287     onInitDrag : function(e){
50288         var data = this.dragData;
50289         this.ddel.innerHTML = this.grid.getDragDropText();
50290         this.proxy.update(this.ddel);
50291         // fire start drag?
50292     },
50293
50294     afterRepair : function(){
50295         this.dragging = false;
50296     },
50297
50298     getRepairXY : function(e, data){
50299         return false;
50300     },
50301
50302     onEndDrag : function(data, e){
50303         // fire end drag?
50304     },
50305
50306     onValidDrop : function(dd, e, id){
50307         // fire drag drop?
50308         this.hideProxy();
50309     },
50310
50311     beforeInvalidDrop : function(e, id){
50312
50313     }
50314 });/*
50315  * Based on:
50316  * Ext JS Library 1.1.1
50317  * Copyright(c) 2006-2007, Ext JS, LLC.
50318  *
50319  * Originally Released Under LGPL - original licence link has changed is not relivant.
50320  *
50321  * Fork - LGPL
50322  * <script type="text/javascript">
50323  */
50324  
50325
50326 /**
50327  * @class Roo.grid.ColumnModel
50328  * @extends Roo.util.Observable
50329  * This is the default implementation of a ColumnModel used by the Grid. It defines
50330  * the columns in the grid.
50331  * <br>Usage:<br>
50332  <pre><code>
50333  var colModel = new Roo.grid.ColumnModel([
50334         {header: "Ticker", width: 60, sortable: true, locked: true},
50335         {header: "Company Name", width: 150, sortable: true},
50336         {header: "Market Cap.", width: 100, sortable: true},
50337         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50338         {header: "Employees", width: 100, sortable: true, resizable: false}
50339  ]);
50340  </code></pre>
50341  * <p>
50342  
50343  * The config options listed for this class are options which may appear in each
50344  * individual column definition.
50345  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50346  * @constructor
50347  * @param {Object} config An Array of column config objects. See this class's
50348  * config objects for details.
50349 */
50350 Roo.grid.ColumnModel = function(config){
50351         /**
50352      * The config passed into the constructor
50353      */
50354     this.config = config;
50355     this.lookup = {};
50356
50357     // if no id, create one
50358     // if the column does not have a dataIndex mapping,
50359     // map it to the order it is in the config
50360     for(var i = 0, len = config.length; i < len; i++){
50361         var c = config[i];
50362         if(typeof c.dataIndex == "undefined"){
50363             c.dataIndex = i;
50364         }
50365         if(typeof c.renderer == "string"){
50366             c.renderer = Roo.util.Format[c.renderer];
50367         }
50368         if(typeof c.id == "undefined"){
50369             c.id = Roo.id();
50370         }
50371         if(c.editor && c.editor.xtype){
50372             c.editor  = Roo.factory(c.editor, Roo.grid);
50373         }
50374         if(c.editor && c.editor.isFormField){
50375             c.editor = new Roo.grid.GridEditor(c.editor);
50376         }
50377         this.lookup[c.id] = c;
50378     }
50379
50380     /**
50381      * The width of columns which have no width specified (defaults to 100)
50382      * @type Number
50383      */
50384     this.defaultWidth = 100;
50385
50386     /**
50387      * Default sortable of columns which have no sortable specified (defaults to false)
50388      * @type Boolean
50389      */
50390     this.defaultSortable = false;
50391
50392     this.addEvents({
50393         /**
50394              * @event widthchange
50395              * Fires when the width of a column changes.
50396              * @param {ColumnModel} this
50397              * @param {Number} columnIndex The column index
50398              * @param {Number} newWidth The new width
50399              */
50400             "widthchange": true,
50401         /**
50402              * @event headerchange
50403              * Fires when the text of a header changes.
50404              * @param {ColumnModel} this
50405              * @param {Number} columnIndex The column index
50406              * @param {Number} newText The new header text
50407              */
50408             "headerchange": true,
50409         /**
50410              * @event hiddenchange
50411              * Fires when a column is hidden or "unhidden".
50412              * @param {ColumnModel} this
50413              * @param {Number} columnIndex The column index
50414              * @param {Boolean} hidden true if hidden, false otherwise
50415              */
50416             "hiddenchange": true,
50417             /**
50418          * @event columnmoved
50419          * Fires when a column is moved.
50420          * @param {ColumnModel} this
50421          * @param {Number} oldIndex
50422          * @param {Number} newIndex
50423          */
50424         "columnmoved" : true,
50425         /**
50426          * @event columlockchange
50427          * Fires when a column's locked state is changed
50428          * @param {ColumnModel} this
50429          * @param {Number} colIndex
50430          * @param {Boolean} locked true if locked
50431          */
50432         "columnlockchange" : true
50433     });
50434     Roo.grid.ColumnModel.superclass.constructor.call(this);
50435 };
50436 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50437     /**
50438      * @cfg {String} header The header text to display in the Grid view.
50439      */
50440     /**
50441      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50442      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50443      * specified, the column's index is used as an index into the Record's data Array.
50444      */
50445     /**
50446      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50447      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50448      */
50449     /**
50450      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50451      * Defaults to the value of the {@link #defaultSortable} property.
50452      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50453      */
50454     /**
50455      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50456      */
50457     /**
50458      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50459      */
50460     /**
50461      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50462      */
50463     /**
50464      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50465      */
50466     /**
50467      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50468      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50469      * default renderer uses the raw data value.
50470      */
50471        /**
50472      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50473      */
50474     /**
50475      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50476      */
50477
50478     /**
50479      * Returns the id of the column at the specified index.
50480      * @param {Number} index The column index
50481      * @return {String} the id
50482      */
50483     getColumnId : function(index){
50484         return this.config[index].id;
50485     },
50486
50487     /**
50488      * Returns the column for a specified id.
50489      * @param {String} id The column id
50490      * @return {Object} the column
50491      */
50492     getColumnById : function(id){
50493         return this.lookup[id];
50494     },
50495
50496     
50497     /**
50498      * Returns the column for a specified dataIndex.
50499      * @param {String} dataIndex The column dataIndex
50500      * @return {Object|Boolean} the column or false if not found
50501      */
50502     getColumnByDataIndex: function(dataIndex){
50503         var index = this.findColumnIndex(dataIndex);
50504         return index > -1 ? this.config[index] : false;
50505     },
50506     
50507     /**
50508      * Returns the index for a specified column id.
50509      * @param {String} id The column id
50510      * @return {Number} the index, or -1 if not found
50511      */
50512     getIndexById : function(id){
50513         for(var i = 0, len = this.config.length; i < len; i++){
50514             if(this.config[i].id == id){
50515                 return i;
50516             }
50517         }
50518         return -1;
50519     },
50520     
50521     /**
50522      * Returns the index for a specified column dataIndex.
50523      * @param {String} dataIndex The column dataIndex
50524      * @return {Number} the index, or -1 if not found
50525      */
50526     
50527     findColumnIndex : function(dataIndex){
50528         for(var i = 0, len = this.config.length; i < len; i++){
50529             if(this.config[i].dataIndex == dataIndex){
50530                 return i;
50531             }
50532         }
50533         return -1;
50534     },
50535     
50536     
50537     moveColumn : function(oldIndex, newIndex){
50538         var c = this.config[oldIndex];
50539         this.config.splice(oldIndex, 1);
50540         this.config.splice(newIndex, 0, c);
50541         this.dataMap = null;
50542         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50543     },
50544
50545     isLocked : function(colIndex){
50546         return this.config[colIndex].locked === true;
50547     },
50548
50549     setLocked : function(colIndex, value, suppressEvent){
50550         if(this.isLocked(colIndex) == value){
50551             return;
50552         }
50553         this.config[colIndex].locked = value;
50554         if(!suppressEvent){
50555             this.fireEvent("columnlockchange", this, colIndex, value);
50556         }
50557     },
50558
50559     getTotalLockedWidth : function(){
50560         var totalWidth = 0;
50561         for(var i = 0; i < this.config.length; i++){
50562             if(this.isLocked(i) && !this.isHidden(i)){
50563                 this.totalWidth += this.getColumnWidth(i);
50564             }
50565         }
50566         return totalWidth;
50567     },
50568
50569     getLockedCount : function(){
50570         for(var i = 0, len = this.config.length; i < len; i++){
50571             if(!this.isLocked(i)){
50572                 return i;
50573             }
50574         }
50575     },
50576
50577     /**
50578      * Returns the number of columns.
50579      * @return {Number}
50580      */
50581     getColumnCount : function(visibleOnly){
50582         if(visibleOnly === true){
50583             var c = 0;
50584             for(var i = 0, len = this.config.length; i < len; i++){
50585                 if(!this.isHidden(i)){
50586                     c++;
50587                 }
50588             }
50589             return c;
50590         }
50591         return this.config.length;
50592     },
50593
50594     /**
50595      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50596      * @param {Function} fn
50597      * @param {Object} scope (optional)
50598      * @return {Array} result
50599      */
50600     getColumnsBy : function(fn, scope){
50601         var r = [];
50602         for(var i = 0, len = this.config.length; i < len; i++){
50603             var c = this.config[i];
50604             if(fn.call(scope||this, c, i) === true){
50605                 r[r.length] = c;
50606             }
50607         }
50608         return r;
50609     },
50610
50611     /**
50612      * Returns true if the specified column is sortable.
50613      * @param {Number} col The column index
50614      * @return {Boolean}
50615      */
50616     isSortable : function(col){
50617         if(typeof this.config[col].sortable == "undefined"){
50618             return this.defaultSortable;
50619         }
50620         return this.config[col].sortable;
50621     },
50622
50623     /**
50624      * Returns the rendering (formatting) function defined for the column.
50625      * @param {Number} col The column index.
50626      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50627      */
50628     getRenderer : function(col){
50629         if(!this.config[col].renderer){
50630             return Roo.grid.ColumnModel.defaultRenderer;
50631         }
50632         return this.config[col].renderer;
50633     },
50634
50635     /**
50636      * Sets the rendering (formatting) function for a column.
50637      * @param {Number} col The column index
50638      * @param {Function} fn The function to use to process the cell's raw data
50639      * to return HTML markup for the grid view. The render function is called with
50640      * the following parameters:<ul>
50641      * <li>Data value.</li>
50642      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50643      * <li>css A CSS style string to apply to the table cell.</li>
50644      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50645      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50646      * <li>Row index</li>
50647      * <li>Column index</li>
50648      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50649      */
50650     setRenderer : function(col, fn){
50651         this.config[col].renderer = fn;
50652     },
50653
50654     /**
50655      * Returns the width for the specified column.
50656      * @param {Number} col The column index
50657      * @return {Number}
50658      */
50659     getColumnWidth : function(col){
50660         return this.config[col].width * 1 || this.defaultWidth;
50661     },
50662
50663     /**
50664      * Sets the width for a column.
50665      * @param {Number} col The column index
50666      * @param {Number} width The new width
50667      */
50668     setColumnWidth : function(col, width, suppressEvent){
50669         this.config[col].width = width;
50670         this.totalWidth = null;
50671         if(!suppressEvent){
50672              this.fireEvent("widthchange", this, col, width);
50673         }
50674     },
50675
50676     /**
50677      * Returns the total width of all columns.
50678      * @param {Boolean} includeHidden True to include hidden column widths
50679      * @return {Number}
50680      */
50681     getTotalWidth : function(includeHidden){
50682         if(!this.totalWidth){
50683             this.totalWidth = 0;
50684             for(var i = 0, len = this.config.length; i < len; i++){
50685                 if(includeHidden || !this.isHidden(i)){
50686                     this.totalWidth += this.getColumnWidth(i);
50687                 }
50688             }
50689         }
50690         return this.totalWidth;
50691     },
50692
50693     /**
50694      * Returns the header for the specified column.
50695      * @param {Number} col The column index
50696      * @return {String}
50697      */
50698     getColumnHeader : function(col){
50699         return this.config[col].header;
50700     },
50701
50702     /**
50703      * Sets the header for a column.
50704      * @param {Number} col The column index
50705      * @param {String} header The new header
50706      */
50707     setColumnHeader : function(col, header){
50708         this.config[col].header = header;
50709         this.fireEvent("headerchange", this, col, header);
50710     },
50711
50712     /**
50713      * Returns the tooltip for the specified column.
50714      * @param {Number} col The column index
50715      * @return {String}
50716      */
50717     getColumnTooltip : function(col){
50718             return this.config[col].tooltip;
50719     },
50720     /**
50721      * Sets the tooltip for a column.
50722      * @param {Number} col The column index
50723      * @param {String} tooltip The new tooltip
50724      */
50725     setColumnTooltip : function(col, tooltip){
50726             this.config[col].tooltip = tooltip;
50727     },
50728
50729     /**
50730      * Returns the dataIndex for the specified column.
50731      * @param {Number} col The column index
50732      * @return {Number}
50733      */
50734     getDataIndex : function(col){
50735         return this.config[col].dataIndex;
50736     },
50737
50738     /**
50739      * Sets the dataIndex for a column.
50740      * @param {Number} col The column index
50741      * @param {Number} dataIndex The new dataIndex
50742      */
50743     setDataIndex : function(col, dataIndex){
50744         this.config[col].dataIndex = dataIndex;
50745     },
50746
50747     
50748     
50749     /**
50750      * Returns true if the cell is editable.
50751      * @param {Number} colIndex The column index
50752      * @param {Number} rowIndex The row index
50753      * @return {Boolean}
50754      */
50755     isCellEditable : function(colIndex, rowIndex){
50756         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50757     },
50758
50759     /**
50760      * Returns the editor defined for the cell/column.
50761      * return false or null to disable editing.
50762      * @param {Number} colIndex The column index
50763      * @param {Number} rowIndex The row index
50764      * @return {Object}
50765      */
50766     getCellEditor : function(colIndex, rowIndex){
50767         return this.config[colIndex].editor;
50768     },
50769
50770     /**
50771      * Sets if a column is editable.
50772      * @param {Number} col The column index
50773      * @param {Boolean} editable True if the column is editable
50774      */
50775     setEditable : function(col, editable){
50776         this.config[col].editable = editable;
50777     },
50778
50779
50780     /**
50781      * Returns true if the column is hidden.
50782      * @param {Number} colIndex The column index
50783      * @return {Boolean}
50784      */
50785     isHidden : function(colIndex){
50786         return this.config[colIndex].hidden;
50787     },
50788
50789
50790     /**
50791      * Returns true if the column width cannot be changed
50792      */
50793     isFixed : function(colIndex){
50794         return this.config[colIndex].fixed;
50795     },
50796
50797     /**
50798      * Returns true if the column can be resized
50799      * @return {Boolean}
50800      */
50801     isResizable : function(colIndex){
50802         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50803     },
50804     /**
50805      * Sets if a column is hidden.
50806      * @param {Number} colIndex The column index
50807      * @param {Boolean} hidden True if the column is hidden
50808      */
50809     setHidden : function(colIndex, hidden){
50810         this.config[colIndex].hidden = hidden;
50811         this.totalWidth = null;
50812         this.fireEvent("hiddenchange", this, colIndex, hidden);
50813     },
50814
50815     /**
50816      * Sets the editor for a column.
50817      * @param {Number} col The column index
50818      * @param {Object} editor The editor object
50819      */
50820     setEditor : function(col, editor){
50821         this.config[col].editor = editor;
50822     }
50823 });
50824
50825 Roo.grid.ColumnModel.defaultRenderer = function(value){
50826         if(typeof value == "string" && value.length < 1){
50827             return "&#160;";
50828         }
50829         return value;
50830 };
50831
50832 // Alias for backwards compatibility
50833 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50834 /*
50835  * Based on:
50836  * Ext JS Library 1.1.1
50837  * Copyright(c) 2006-2007, Ext JS, LLC.
50838  *
50839  * Originally Released Under LGPL - original licence link has changed is not relivant.
50840  *
50841  * Fork - LGPL
50842  * <script type="text/javascript">
50843  */
50844
50845 /**
50846  * @class Roo.grid.AbstractSelectionModel
50847  * @extends Roo.util.Observable
50848  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50849  * implemented by descendant classes.  This class should not be directly instantiated.
50850  * @constructor
50851  */
50852 Roo.grid.AbstractSelectionModel = function(){
50853     this.locked = false;
50854     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50855 };
50856
50857 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50858     /** @ignore Called by the grid automatically. Do not call directly. */
50859     init : function(grid){
50860         this.grid = grid;
50861         this.initEvents();
50862     },
50863
50864     /**
50865      * Locks the selections.
50866      */
50867     lock : function(){
50868         this.locked = true;
50869     },
50870
50871     /**
50872      * Unlocks the selections.
50873      */
50874     unlock : function(){
50875         this.locked = false;
50876     },
50877
50878     /**
50879      * Returns true if the selections are locked.
50880      * @return {Boolean}
50881      */
50882     isLocked : function(){
50883         return this.locked;
50884     }
50885 });/*
50886  * Based on:
50887  * Ext JS Library 1.1.1
50888  * Copyright(c) 2006-2007, Ext JS, LLC.
50889  *
50890  * Originally Released Under LGPL - original licence link has changed is not relivant.
50891  *
50892  * Fork - LGPL
50893  * <script type="text/javascript">
50894  */
50895 /**
50896  * @extends Roo.grid.AbstractSelectionModel
50897  * @class Roo.grid.RowSelectionModel
50898  * The default SelectionModel used by {@link Roo.grid.Grid}.
50899  * It supports multiple selections and keyboard selection/navigation. 
50900  * @constructor
50901  * @param {Object} config
50902  */
50903 Roo.grid.RowSelectionModel = function(config){
50904     Roo.apply(this, config);
50905     this.selections = new Roo.util.MixedCollection(false, function(o){
50906         return o.id;
50907     });
50908
50909     this.last = false;
50910     this.lastActive = false;
50911
50912     this.addEvents({
50913         /**
50914              * @event selectionchange
50915              * Fires when the selection changes
50916              * @param {SelectionModel} this
50917              */
50918             "selectionchange" : true,
50919         /**
50920              * @event afterselectionchange
50921              * Fires after the selection changes (eg. by key press or clicking)
50922              * @param {SelectionModel} this
50923              */
50924             "afterselectionchange" : true,
50925         /**
50926              * @event beforerowselect
50927              * Fires when a row is selected being selected, return false to cancel.
50928              * @param {SelectionModel} this
50929              * @param {Number} rowIndex The selected index
50930              * @param {Boolean} keepExisting False if other selections will be cleared
50931              */
50932             "beforerowselect" : true,
50933         /**
50934              * @event rowselect
50935              * Fires when a row is selected.
50936              * @param {SelectionModel} this
50937              * @param {Number} rowIndex The selected index
50938              * @param {Roo.data.Record} r The record
50939              */
50940             "rowselect" : true,
50941         /**
50942              * @event rowdeselect
50943              * Fires when a row is deselected.
50944              * @param {SelectionModel} this
50945              * @param {Number} rowIndex The selected index
50946              */
50947         "rowdeselect" : true
50948     });
50949     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50950     this.locked = false;
50951 };
50952
50953 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50954     /**
50955      * @cfg {Boolean} singleSelect
50956      * True to allow selection of only one row at a time (defaults to false)
50957      */
50958     singleSelect : false,
50959
50960     // private
50961     initEvents : function(){
50962
50963         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50964             this.grid.on("mousedown", this.handleMouseDown, this);
50965         }else{ // allow click to work like normal
50966             this.grid.on("rowclick", this.handleDragableRowClick, this);
50967         }
50968
50969         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50970             "up" : function(e){
50971                 if(!e.shiftKey){
50972                     this.selectPrevious(e.shiftKey);
50973                 }else if(this.last !== false && this.lastActive !== false){
50974                     var last = this.last;
50975                     this.selectRange(this.last,  this.lastActive-1);
50976                     this.grid.getView().focusRow(this.lastActive);
50977                     if(last !== false){
50978                         this.last = last;
50979                     }
50980                 }else{
50981                     this.selectFirstRow();
50982                 }
50983                 this.fireEvent("afterselectionchange", this);
50984             },
50985             "down" : function(e){
50986                 if(!e.shiftKey){
50987                     this.selectNext(e.shiftKey);
50988                 }else if(this.last !== false && this.lastActive !== false){
50989                     var last = this.last;
50990                     this.selectRange(this.last,  this.lastActive+1);
50991                     this.grid.getView().focusRow(this.lastActive);
50992                     if(last !== false){
50993                         this.last = last;
50994                     }
50995                 }else{
50996                     this.selectFirstRow();
50997                 }
50998                 this.fireEvent("afterselectionchange", this);
50999             },
51000             scope: this
51001         });
51002
51003         var view = this.grid.view;
51004         view.on("refresh", this.onRefresh, this);
51005         view.on("rowupdated", this.onRowUpdated, this);
51006         view.on("rowremoved", this.onRemove, this);
51007     },
51008
51009     // private
51010     onRefresh : function(){
51011         var ds = this.grid.dataSource, i, v = this.grid.view;
51012         var s = this.selections;
51013         s.each(function(r){
51014             if((i = ds.indexOfId(r.id)) != -1){
51015                 v.onRowSelect(i);
51016             }else{
51017                 s.remove(r);
51018             }
51019         });
51020     },
51021
51022     // private
51023     onRemove : function(v, index, r){
51024         this.selections.remove(r);
51025     },
51026
51027     // private
51028     onRowUpdated : function(v, index, r){
51029         if(this.isSelected(r)){
51030             v.onRowSelect(index);
51031         }
51032     },
51033
51034     /**
51035      * Select records.
51036      * @param {Array} records The records to select
51037      * @param {Boolean} keepExisting (optional) True to keep existing selections
51038      */
51039     selectRecords : function(records, keepExisting){
51040         if(!keepExisting){
51041             this.clearSelections();
51042         }
51043         var ds = this.grid.dataSource;
51044         for(var i = 0, len = records.length; i < len; i++){
51045             this.selectRow(ds.indexOf(records[i]), true);
51046         }
51047     },
51048
51049     /**
51050      * Gets the number of selected rows.
51051      * @return {Number}
51052      */
51053     getCount : function(){
51054         return this.selections.length;
51055     },
51056
51057     /**
51058      * Selects the first row in the grid.
51059      */
51060     selectFirstRow : function(){
51061         this.selectRow(0);
51062     },
51063
51064     /**
51065      * Select the last row.
51066      * @param {Boolean} keepExisting (optional) True to keep existing selections
51067      */
51068     selectLastRow : function(keepExisting){
51069         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
51070     },
51071
51072     /**
51073      * Selects the row immediately following the last selected row.
51074      * @param {Boolean} keepExisting (optional) True to keep existing selections
51075      */
51076     selectNext : function(keepExisting){
51077         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
51078             this.selectRow(this.last+1, keepExisting);
51079             this.grid.getView().focusRow(this.last);
51080         }
51081     },
51082
51083     /**
51084      * Selects the row that precedes the last selected row.
51085      * @param {Boolean} keepExisting (optional) True to keep existing selections
51086      */
51087     selectPrevious : function(keepExisting){
51088         if(this.last){
51089             this.selectRow(this.last-1, keepExisting);
51090             this.grid.getView().focusRow(this.last);
51091         }
51092     },
51093
51094     /**
51095      * Returns the selected records
51096      * @return {Array} Array of selected records
51097      */
51098     getSelections : function(){
51099         return [].concat(this.selections.items);
51100     },
51101
51102     /**
51103      * Returns the first selected record.
51104      * @return {Record}
51105      */
51106     getSelected : function(){
51107         return this.selections.itemAt(0);
51108     },
51109
51110
51111     /**
51112      * Clears all selections.
51113      */
51114     clearSelections : function(fast){
51115         if(this.locked) return;
51116         if(fast !== true){
51117             var ds = this.grid.dataSource;
51118             var s = this.selections;
51119             s.each(function(r){
51120                 this.deselectRow(ds.indexOfId(r.id));
51121             }, this);
51122             s.clear();
51123         }else{
51124             this.selections.clear();
51125         }
51126         this.last = false;
51127     },
51128
51129
51130     /**
51131      * Selects all rows.
51132      */
51133     selectAll : function(){
51134         if(this.locked) return;
51135         this.selections.clear();
51136         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51137             this.selectRow(i, true);
51138         }
51139     },
51140
51141     /**
51142      * Returns True if there is a selection.
51143      * @return {Boolean}
51144      */
51145     hasSelection : function(){
51146         return this.selections.length > 0;
51147     },
51148
51149     /**
51150      * Returns True if the specified row is selected.
51151      * @param {Number/Record} record The record or index of the record to check
51152      * @return {Boolean}
51153      */
51154     isSelected : function(index){
51155         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51156         return (r && this.selections.key(r.id) ? true : false);
51157     },
51158
51159     /**
51160      * Returns True if the specified record id is selected.
51161      * @param {String} id The id of record to check
51162      * @return {Boolean}
51163      */
51164     isIdSelected : function(id){
51165         return (this.selections.key(id) ? true : false);
51166     },
51167
51168     // private
51169     handleMouseDown : function(e, t){
51170         var view = this.grid.getView(), rowIndex;
51171         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51172             return;
51173         };
51174         if(e.shiftKey && this.last !== false){
51175             var last = this.last;
51176             this.selectRange(last, rowIndex, e.ctrlKey);
51177             this.last = last; // reset the last
51178             view.focusRow(rowIndex);
51179         }else{
51180             var isSelected = this.isSelected(rowIndex);
51181             if(e.button !== 0 && isSelected){
51182                 view.focusRow(rowIndex);
51183             }else if(e.ctrlKey && isSelected){
51184                 this.deselectRow(rowIndex);
51185             }else if(!isSelected){
51186                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51187                 view.focusRow(rowIndex);
51188             }
51189         }
51190         this.fireEvent("afterselectionchange", this);
51191     },
51192     // private
51193     handleDragableRowClick :  function(grid, rowIndex, e) 
51194     {
51195         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51196             this.selectRow(rowIndex, false);
51197             grid.view.focusRow(rowIndex);
51198              this.fireEvent("afterselectionchange", this);
51199         }
51200     },
51201     
51202     /**
51203      * Selects multiple rows.
51204      * @param {Array} rows Array of the indexes of the row to select
51205      * @param {Boolean} keepExisting (optional) True to keep existing selections
51206      */
51207     selectRows : function(rows, keepExisting){
51208         if(!keepExisting){
51209             this.clearSelections();
51210         }
51211         for(var i = 0, len = rows.length; i < len; i++){
51212             this.selectRow(rows[i], true);
51213         }
51214     },
51215
51216     /**
51217      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51218      * @param {Number} startRow The index of the first row in the range
51219      * @param {Number} endRow The index of the last row in the range
51220      * @param {Boolean} keepExisting (optional) True to retain existing selections
51221      */
51222     selectRange : function(startRow, endRow, keepExisting){
51223         if(this.locked) return;
51224         if(!keepExisting){
51225             this.clearSelections();
51226         }
51227         if(startRow <= endRow){
51228             for(var i = startRow; i <= endRow; i++){
51229                 this.selectRow(i, true);
51230             }
51231         }else{
51232             for(var i = startRow; i >= endRow; i--){
51233                 this.selectRow(i, true);
51234             }
51235         }
51236     },
51237
51238     /**
51239      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51240      * @param {Number} startRow The index of the first row in the range
51241      * @param {Number} endRow The index of the last row in the range
51242      */
51243     deselectRange : function(startRow, endRow, preventViewNotify){
51244         if(this.locked) return;
51245         for(var i = startRow; i <= endRow; i++){
51246             this.deselectRow(i, preventViewNotify);
51247         }
51248     },
51249
51250     /**
51251      * Selects a row.
51252      * @param {Number} row The index of the row to select
51253      * @param {Boolean} keepExisting (optional) True to keep existing selections
51254      */
51255     selectRow : function(index, keepExisting, preventViewNotify){
51256         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51257         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51258             if(!keepExisting || this.singleSelect){
51259                 this.clearSelections();
51260             }
51261             var r = this.grid.dataSource.getAt(index);
51262             this.selections.add(r);
51263             this.last = this.lastActive = index;
51264             if(!preventViewNotify){
51265                 this.grid.getView().onRowSelect(index);
51266             }
51267             this.fireEvent("rowselect", this, index, r);
51268             this.fireEvent("selectionchange", this);
51269         }
51270     },
51271
51272     /**
51273      * Deselects a row.
51274      * @param {Number} row The index of the row to deselect
51275      */
51276     deselectRow : function(index, preventViewNotify){
51277         if(this.locked) return;
51278         if(this.last == index){
51279             this.last = false;
51280         }
51281         if(this.lastActive == index){
51282             this.lastActive = false;
51283         }
51284         var r = this.grid.dataSource.getAt(index);
51285         this.selections.remove(r);
51286         if(!preventViewNotify){
51287             this.grid.getView().onRowDeselect(index);
51288         }
51289         this.fireEvent("rowdeselect", this, index);
51290         this.fireEvent("selectionchange", this);
51291     },
51292
51293     // private
51294     restoreLast : function(){
51295         if(this._last){
51296             this.last = this._last;
51297         }
51298     },
51299
51300     // private
51301     acceptsNav : function(row, col, cm){
51302         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51303     },
51304
51305     // private
51306     onEditorKey : function(field, e){
51307         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51308         if(k == e.TAB){
51309             e.stopEvent();
51310             ed.completeEdit();
51311             if(e.shiftKey){
51312                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51313             }else{
51314                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51315             }
51316         }else if(k == e.ENTER && !e.ctrlKey){
51317             e.stopEvent();
51318             ed.completeEdit();
51319             if(e.shiftKey){
51320                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51321             }else{
51322                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51323             }
51324         }else if(k == e.ESC){
51325             ed.cancelEdit();
51326         }
51327         if(newCell){
51328             g.startEditing(newCell[0], newCell[1]);
51329         }
51330     }
51331 });/*
51332  * Based on:
51333  * Ext JS Library 1.1.1
51334  * Copyright(c) 2006-2007, Ext JS, LLC.
51335  *
51336  * Originally Released Under LGPL - original licence link has changed is not relivant.
51337  *
51338  * Fork - LGPL
51339  * <script type="text/javascript">
51340  */
51341 /**
51342  * @class Roo.grid.CellSelectionModel
51343  * @extends Roo.grid.AbstractSelectionModel
51344  * This class provides the basic implementation for cell selection in a grid.
51345  * @constructor
51346  * @param {Object} config The object containing the configuration of this model.
51347  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51348  */
51349 Roo.grid.CellSelectionModel = function(config){
51350     Roo.apply(this, config);
51351
51352     this.selection = null;
51353
51354     this.addEvents({
51355         /**
51356              * @event beforerowselect
51357              * Fires before a cell is selected.
51358              * @param {SelectionModel} this
51359              * @param {Number} rowIndex The selected row index
51360              * @param {Number} colIndex The selected cell index
51361              */
51362             "beforecellselect" : true,
51363         /**
51364              * @event cellselect
51365              * Fires when a cell is selected.
51366              * @param {SelectionModel} this
51367              * @param {Number} rowIndex The selected row index
51368              * @param {Number} colIndex The selected cell index
51369              */
51370             "cellselect" : true,
51371         /**
51372              * @event selectionchange
51373              * Fires when the active selection changes.
51374              * @param {SelectionModel} this
51375              * @param {Object} selection null for no selection or an object (o) with two properties
51376                 <ul>
51377                 <li>o.record: the record object for the row the selection is in</li>
51378                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51379                 </ul>
51380              */
51381             "selectionchange" : true,
51382         /**
51383              * @event tabend
51384              * Fires when the tab (or enter) was pressed on the last editable cell
51385              * You can use this to trigger add new row.
51386              * @param {SelectionModel} this
51387              */
51388             "tabend" : true,
51389          /**
51390              * @event beforeeditnext
51391              * Fires before the next editable sell is made active
51392              * You can use this to skip to another cell or fire the tabend
51393              *    if you set cell to false
51394              * @param {Object} eventdata object : { cell : [ row, col ] } 
51395              */
51396             "beforeeditnext" : true
51397     });
51398     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51399 };
51400
51401 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51402     
51403     enter_is_tab: false,
51404
51405     /** @ignore */
51406     initEvents : function(){
51407         this.grid.on("mousedown", this.handleMouseDown, this);
51408         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51409         var view = this.grid.view;
51410         view.on("refresh", this.onViewChange, this);
51411         view.on("rowupdated", this.onRowUpdated, this);
51412         view.on("beforerowremoved", this.clearSelections, this);
51413         view.on("beforerowsinserted", this.clearSelections, this);
51414         if(this.grid.isEditor){
51415             this.grid.on("beforeedit", this.beforeEdit,  this);
51416         }
51417     },
51418
51419         //private
51420     beforeEdit : function(e){
51421         this.select(e.row, e.column, false, true, e.record);
51422     },
51423
51424         //private
51425     onRowUpdated : function(v, index, r){
51426         if(this.selection && this.selection.record == r){
51427             v.onCellSelect(index, this.selection.cell[1]);
51428         }
51429     },
51430
51431         //private
51432     onViewChange : function(){
51433         this.clearSelections(true);
51434     },
51435
51436         /**
51437          * Returns the currently selected cell,.
51438          * @return {Array} The selected cell (row, column) or null if none selected.
51439          */
51440     getSelectedCell : function(){
51441         return this.selection ? this.selection.cell : null;
51442     },
51443
51444     /**
51445      * Clears all selections.
51446      * @param {Boolean} true to prevent the gridview from being notified about the change.
51447      */
51448     clearSelections : function(preventNotify){
51449         var s = this.selection;
51450         if(s){
51451             if(preventNotify !== true){
51452                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51453             }
51454             this.selection = null;
51455             this.fireEvent("selectionchange", this, null);
51456         }
51457     },
51458
51459     /**
51460      * Returns true if there is a selection.
51461      * @return {Boolean}
51462      */
51463     hasSelection : function(){
51464         return this.selection ? true : false;
51465     },
51466
51467     /** @ignore */
51468     handleMouseDown : function(e, t){
51469         var v = this.grid.getView();
51470         if(this.isLocked()){
51471             return;
51472         };
51473         var row = v.findRowIndex(t);
51474         var cell = v.findCellIndex(t);
51475         if(row !== false && cell !== false){
51476             this.select(row, cell);
51477         }
51478     },
51479
51480     /**
51481      * Selects a cell.
51482      * @param {Number} rowIndex
51483      * @param {Number} collIndex
51484      */
51485     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51486         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51487             this.clearSelections();
51488             r = r || this.grid.dataSource.getAt(rowIndex);
51489             this.selection = {
51490                 record : r,
51491                 cell : [rowIndex, colIndex]
51492             };
51493             if(!preventViewNotify){
51494                 var v = this.grid.getView();
51495                 v.onCellSelect(rowIndex, colIndex);
51496                 if(preventFocus !== true){
51497                     v.focusCell(rowIndex, colIndex);
51498                 }
51499             }
51500             this.fireEvent("cellselect", this, rowIndex, colIndex);
51501             this.fireEvent("selectionchange", this, this.selection);
51502         }
51503     },
51504
51505         //private
51506     isSelectable : function(rowIndex, colIndex, cm){
51507         return !cm.isHidden(colIndex);
51508     },
51509
51510     /** @ignore */
51511     handleKeyDown : function(e){
51512         //Roo.log('Cell Sel Model handleKeyDown');
51513         if(!e.isNavKeyPress()){
51514             return;
51515         }
51516         var g = this.grid, s = this.selection;
51517         if(!s){
51518             e.stopEvent();
51519             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51520             if(cell){
51521                 this.select(cell[0], cell[1]);
51522             }
51523             return;
51524         }
51525         var sm = this;
51526         var walk = function(row, col, step){
51527             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51528         };
51529         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51530         var newCell;
51531
51532       
51533
51534         switch(k){
51535             case e.TAB:
51536                 // handled by onEditorKey
51537                 if (g.isEditor && g.editing) {
51538                     return;
51539                 }
51540                 if(e.shiftKey) {
51541                     newCell = walk(r, c-1, -1);
51542                 } else {
51543                     newCell = walk(r, c+1, 1);
51544                 }
51545                 break;
51546             
51547             case e.DOWN:
51548                newCell = walk(r+1, c, 1);
51549                 break;
51550             
51551             case e.UP:
51552                 newCell = walk(r-1, c, -1);
51553                 break;
51554             
51555             case e.RIGHT:
51556                 newCell = walk(r, c+1, 1);
51557                 break;
51558             
51559             case e.LEFT:
51560                 newCell = walk(r, c-1, -1);
51561                 break;
51562             
51563             case e.ENTER:
51564                 
51565                 if(g.isEditor && !g.editing){
51566                    g.startEditing(r, c);
51567                    e.stopEvent();
51568                    return;
51569                 }
51570                 
51571                 
51572              break;
51573         };
51574         if(newCell){
51575             this.select(newCell[0], newCell[1]);
51576             e.stopEvent();
51577             
51578         }
51579     },
51580
51581     acceptsNav : function(row, col, cm){
51582         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51583     },
51584     /**
51585      * Selects a cell.
51586      * @param {Number} field (not used) - as it's normally used as a listener
51587      * @param {Number} e - event - fake it by using
51588      *
51589      * var e = Roo.EventObjectImpl.prototype;
51590      * e.keyCode = e.TAB
51591      *
51592      * 
51593      */
51594     onEditorKey : function(field, e){
51595         
51596         var k = e.getKey(),
51597             newCell,
51598             g = this.grid,
51599             ed = g.activeEditor,
51600             forward = false;
51601         ///Roo.log('onEditorKey' + k);
51602         
51603         
51604         if (this.enter_is_tab && k == e.ENTER) {
51605             k = e.TAB;
51606         }
51607         
51608         if(k == e.TAB){
51609             if(e.shiftKey){
51610                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51611             }else{
51612                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51613                 forward = true;
51614             }
51615             
51616             e.stopEvent();
51617             
51618         }else if(k == e.ENTER &&  !e.ctrlKey){
51619             ed.completeEdit();
51620             e.stopEvent();
51621             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51622         }else if(k == e.ESC){
51623             ed.cancelEdit();
51624         }
51625         if (newCell) {
51626             var ecall = { cell : newCell } 
51627             this.fireEvent('beforeeditnext', ecall );
51628             newCell = ecall.cell;
51629         }
51630         if(newCell){
51631             // can modify new Cell
51632
51633             
51634             //Roo.log('next cell after edit');
51635             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51636         } else if (forward) {
51637             // tabbed past last
51638             this.fireEvent.defer(100, this, ['tabend',this]);
51639         }
51640     }
51641 });/*
51642  * Based on:
51643  * Ext JS Library 1.1.1
51644  * Copyright(c) 2006-2007, Ext JS, LLC.
51645  *
51646  * Originally Released Under LGPL - original licence link has changed is not relivant.
51647  *
51648  * Fork - LGPL
51649  * <script type="text/javascript">
51650  */
51651  
51652 /**
51653  * @class Roo.grid.EditorGrid
51654  * @extends Roo.grid.Grid
51655  * Class for creating and editable grid.
51656  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51657  * The container MUST have some type of size defined for the grid to fill. The container will be 
51658  * automatically set to position relative if it isn't already.
51659  * @param {Object} dataSource The data model to bind to
51660  * @param {Object} colModel The column model with info about this grid's columns
51661  */
51662 Roo.grid.EditorGrid = function(container, config){
51663     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51664     this.getGridEl().addClass("xedit-grid");
51665
51666     if(!this.selModel){
51667         this.selModel = new Roo.grid.CellSelectionModel();
51668     }
51669
51670     this.activeEditor = null;
51671
51672         this.addEvents({
51673             /**
51674              * @event beforeedit
51675              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51676              * <ul style="padding:5px;padding-left:16px;">
51677              * <li>grid - This grid</li>
51678              * <li>record - The record being edited</li>
51679              * <li>field - The field name being edited</li>
51680              * <li>value - The value for the field being edited.</li>
51681              * <li>row - The grid row index</li>
51682              * <li>column - The grid column index</li>
51683              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51684              * </ul>
51685              * @param {Object} e An edit event (see above for description)
51686              */
51687             "beforeedit" : true,
51688             /**
51689              * @event afteredit
51690              * Fires after a cell is edited. <br />
51691              * <ul style="padding:5px;padding-left:16px;">
51692              * <li>grid - This grid</li>
51693              * <li>record - The record being edited</li>
51694              * <li>field - The field name being edited</li>
51695              * <li>value - The value being set</li>
51696              * <li>originalValue - The original value for the field, before the edit.</li>
51697              * <li>row - The grid row index</li>
51698              * <li>column - The grid column index</li>
51699              * </ul>
51700              * @param {Object} e An edit event (see above for description)
51701              */
51702             "afteredit" : true,
51703             /**
51704              * @event validateedit
51705              * Fires after a cell is edited, but before the value is set in the record. 
51706          * You can use this to modify the value being set in the field, Return false
51707              * to cancel the change. The edit event object has the following properties <br />
51708              * <ul style="padding:5px;padding-left:16px;">
51709          * <li>editor - This editor</li>
51710              * <li>grid - This grid</li>
51711              * <li>record - The record being edited</li>
51712              * <li>field - The field name being edited</li>
51713              * <li>value - The value being set</li>
51714              * <li>originalValue - The original value for the field, before the edit.</li>
51715              * <li>row - The grid row index</li>
51716              * <li>column - The grid column index</li>
51717              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51718              * </ul>
51719              * @param {Object} e An edit event (see above for description)
51720              */
51721             "validateedit" : true
51722         });
51723     this.on("bodyscroll", this.stopEditing,  this);
51724     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51725 };
51726
51727 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51728     /**
51729      * @cfg {Number} clicksToEdit
51730      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51731      */
51732     clicksToEdit: 2,
51733
51734     // private
51735     isEditor : true,
51736     // private
51737     trackMouseOver: false, // causes very odd FF errors
51738
51739     onCellDblClick : function(g, row, col){
51740         this.startEditing(row, col);
51741     },
51742
51743     onEditComplete : function(ed, value, startValue){
51744         this.editing = false;
51745         this.activeEditor = null;
51746         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51747         var r = ed.record;
51748         var field = this.colModel.getDataIndex(ed.col);
51749         var e = {
51750             grid: this,
51751             record: r,
51752             field: field,
51753             originalValue: startValue,
51754             value: value,
51755             row: ed.row,
51756             column: ed.col,
51757             cancel:false,
51758             editor: ed
51759         };
51760         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51761         cell.show();
51762           
51763         if(String(value) !== String(startValue)){
51764             
51765             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51766                 r.set(field, e.value);
51767                 // if we are dealing with a combo box..
51768                 // then we also set the 'name' colum to be the displayField
51769                 if (ed.field.displayField && ed.field.name) {
51770                     r.set(ed.field.name, ed.field.el.dom.value);
51771                 }
51772                 
51773                 delete e.cancel; //?? why!!!
51774                 this.fireEvent("afteredit", e);
51775             }
51776         } else {
51777             this.fireEvent("afteredit", e); // always fire it!
51778         }
51779         this.view.focusCell(ed.row, ed.col);
51780     },
51781
51782     /**
51783      * Starts editing the specified for the specified row/column
51784      * @param {Number} rowIndex
51785      * @param {Number} colIndex
51786      */
51787     startEditing : function(row, col){
51788         this.stopEditing();
51789         if(this.colModel.isCellEditable(col, row)){
51790             this.view.ensureVisible(row, col, true);
51791           
51792             var r = this.dataSource.getAt(row);
51793             var field = this.colModel.getDataIndex(col);
51794             var cell = Roo.get(this.view.getCell(row,col));
51795             var e = {
51796                 grid: this,
51797                 record: r,
51798                 field: field,
51799                 value: r.data[field],
51800                 row: row,
51801                 column: col,
51802                 cancel:false 
51803             };
51804             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51805                 this.editing = true;
51806                 var ed = this.colModel.getCellEditor(col, row);
51807                 
51808                 if (!ed) {
51809                     return;
51810                 }
51811                 if(!ed.rendered){
51812                     ed.render(ed.parentEl || document.body);
51813                 }
51814                 ed.field.reset();
51815                
51816                 cell.hide();
51817                 
51818                 (function(){ // complex but required for focus issues in safari, ie and opera
51819                     ed.row = row;
51820                     ed.col = col;
51821                     ed.record = r;
51822                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51823                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51824                     this.activeEditor = ed;
51825                     var v = r.data[field];
51826                     ed.startEdit(this.view.getCell(row, col), v);
51827                     // combo's with 'displayField and name set
51828                     if (ed.field.displayField && ed.field.name) {
51829                         ed.field.el.dom.value = r.data[ed.field.name];
51830                     }
51831                     
51832                     
51833                 }).defer(50, this);
51834             }
51835         }
51836     },
51837         
51838     /**
51839      * Stops any active editing
51840      */
51841     stopEditing : function(){
51842         if(this.activeEditor){
51843             this.activeEditor.completeEdit();
51844         }
51845         this.activeEditor = null;
51846     }
51847 });/*
51848  * Based on:
51849  * Ext JS Library 1.1.1
51850  * Copyright(c) 2006-2007, Ext JS, LLC.
51851  *
51852  * Originally Released Under LGPL - original licence link has changed is not relivant.
51853  *
51854  * Fork - LGPL
51855  * <script type="text/javascript">
51856  */
51857
51858 // private - not really -- you end up using it !
51859 // This is a support class used internally by the Grid components
51860
51861 /**
51862  * @class Roo.grid.GridEditor
51863  * @extends Roo.Editor
51864  * Class for creating and editable grid elements.
51865  * @param {Object} config any settings (must include field)
51866  */
51867 Roo.grid.GridEditor = function(field, config){
51868     if (!config && field.field) {
51869         config = field;
51870         field = Roo.factory(config.field, Roo.form);
51871     }
51872     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51873     field.monitorTab = false;
51874 };
51875
51876 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51877     
51878     /**
51879      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51880      */
51881     
51882     alignment: "tl-tl",
51883     autoSize: "width",
51884     hideEl : false,
51885     cls: "x-small-editor x-grid-editor",
51886     shim:false,
51887     shadow:"frame"
51888 });/*
51889  * Based on:
51890  * Ext JS Library 1.1.1
51891  * Copyright(c) 2006-2007, Ext JS, LLC.
51892  *
51893  * Originally Released Under LGPL - original licence link has changed is not relivant.
51894  *
51895  * Fork - LGPL
51896  * <script type="text/javascript">
51897  */
51898   
51899
51900   
51901 Roo.grid.PropertyRecord = Roo.data.Record.create([
51902     {name:'name',type:'string'},  'value'
51903 ]);
51904
51905
51906 Roo.grid.PropertyStore = function(grid, source){
51907     this.grid = grid;
51908     this.store = new Roo.data.Store({
51909         recordType : Roo.grid.PropertyRecord
51910     });
51911     this.store.on('update', this.onUpdate,  this);
51912     if(source){
51913         this.setSource(source);
51914     }
51915     Roo.grid.PropertyStore.superclass.constructor.call(this);
51916 };
51917
51918
51919
51920 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51921     setSource : function(o){
51922         this.source = o;
51923         this.store.removeAll();
51924         var data = [];
51925         for(var k in o){
51926             if(this.isEditableValue(o[k])){
51927                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51928             }
51929         }
51930         this.store.loadRecords({records: data}, {}, true);
51931     },
51932
51933     onUpdate : function(ds, record, type){
51934         if(type == Roo.data.Record.EDIT){
51935             var v = record.data['value'];
51936             var oldValue = record.modified['value'];
51937             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51938                 this.source[record.id] = v;
51939                 record.commit();
51940                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51941             }else{
51942                 record.reject();
51943             }
51944         }
51945     },
51946
51947     getProperty : function(row){
51948        return this.store.getAt(row);
51949     },
51950
51951     isEditableValue: function(val){
51952         if(val && val instanceof Date){
51953             return true;
51954         }else if(typeof val == 'object' || typeof val == 'function'){
51955             return false;
51956         }
51957         return true;
51958     },
51959
51960     setValue : function(prop, value){
51961         this.source[prop] = value;
51962         this.store.getById(prop).set('value', value);
51963     },
51964
51965     getSource : function(){
51966         return this.source;
51967     }
51968 });
51969
51970 Roo.grid.PropertyColumnModel = function(grid, store){
51971     this.grid = grid;
51972     var g = Roo.grid;
51973     g.PropertyColumnModel.superclass.constructor.call(this, [
51974         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51975         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51976     ]);
51977     this.store = store;
51978     this.bselect = Roo.DomHelper.append(document.body, {
51979         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51980             {tag: 'option', value: 'true', html: 'true'},
51981             {tag: 'option', value: 'false', html: 'false'}
51982         ]
51983     });
51984     Roo.id(this.bselect);
51985     var f = Roo.form;
51986     this.editors = {
51987         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51988         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51989         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51990         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51991         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51992     };
51993     this.renderCellDelegate = this.renderCell.createDelegate(this);
51994     this.renderPropDelegate = this.renderProp.createDelegate(this);
51995 };
51996
51997 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51998     
51999     
52000     nameText : 'Name',
52001     valueText : 'Value',
52002     
52003     dateFormat : 'm/j/Y',
52004     
52005     
52006     renderDate : function(dateVal){
52007         return dateVal.dateFormat(this.dateFormat);
52008     },
52009
52010     renderBool : function(bVal){
52011         return bVal ? 'true' : 'false';
52012     },
52013
52014     isCellEditable : function(colIndex, rowIndex){
52015         return colIndex == 1;
52016     },
52017
52018     getRenderer : function(col){
52019         return col == 1 ?
52020             this.renderCellDelegate : this.renderPropDelegate;
52021     },
52022
52023     renderProp : function(v){
52024         return this.getPropertyName(v);
52025     },
52026
52027     renderCell : function(val){
52028         var rv = val;
52029         if(val instanceof Date){
52030             rv = this.renderDate(val);
52031         }else if(typeof val == 'boolean'){
52032             rv = this.renderBool(val);
52033         }
52034         return Roo.util.Format.htmlEncode(rv);
52035     },
52036
52037     getPropertyName : function(name){
52038         var pn = this.grid.propertyNames;
52039         return pn && pn[name] ? pn[name] : name;
52040     },
52041
52042     getCellEditor : function(colIndex, rowIndex){
52043         var p = this.store.getProperty(rowIndex);
52044         var n = p.data['name'], val = p.data['value'];
52045         
52046         if(typeof(this.grid.customEditors[n]) == 'string'){
52047             return this.editors[this.grid.customEditors[n]];
52048         }
52049         if(typeof(this.grid.customEditors[n]) != 'undefined'){
52050             return this.grid.customEditors[n];
52051         }
52052         if(val instanceof Date){
52053             return this.editors['date'];
52054         }else if(typeof val == 'number'){
52055             return this.editors['number'];
52056         }else if(typeof val == 'boolean'){
52057             return this.editors['boolean'];
52058         }else{
52059             return this.editors['string'];
52060         }
52061     }
52062 });
52063
52064 /**
52065  * @class Roo.grid.PropertyGrid
52066  * @extends Roo.grid.EditorGrid
52067  * This class represents the  interface of a component based property grid control.
52068  * <br><br>Usage:<pre><code>
52069  var grid = new Roo.grid.PropertyGrid("my-container-id", {
52070       
52071  });
52072  // set any options
52073  grid.render();
52074  * </code></pre>
52075   
52076  * @constructor
52077  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52078  * The container MUST have some type of size defined for the grid to fill. The container will be
52079  * automatically set to position relative if it isn't already.
52080  * @param {Object} config A config object that sets properties on this grid.
52081  */
52082 Roo.grid.PropertyGrid = function(container, config){
52083     config = config || {};
52084     var store = new Roo.grid.PropertyStore(this);
52085     this.store = store;
52086     var cm = new Roo.grid.PropertyColumnModel(this, store);
52087     store.store.sort('name', 'ASC');
52088     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
52089         ds: store.store,
52090         cm: cm,
52091         enableColLock:false,
52092         enableColumnMove:false,
52093         stripeRows:false,
52094         trackMouseOver: false,
52095         clicksToEdit:1
52096     }, config));
52097     this.getGridEl().addClass('x-props-grid');
52098     this.lastEditRow = null;
52099     this.on('columnresize', this.onColumnResize, this);
52100     this.addEvents({
52101          /**
52102              * @event beforepropertychange
52103              * Fires before a property changes (return false to stop?)
52104              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52105              * @param {String} id Record Id
52106              * @param {String} newval New Value
52107          * @param {String} oldval Old Value
52108              */
52109         "beforepropertychange": true,
52110         /**
52111              * @event propertychange
52112              * Fires after a property changes
52113              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52114              * @param {String} id Record Id
52115              * @param {String} newval New Value
52116          * @param {String} oldval Old Value
52117              */
52118         "propertychange": true
52119     });
52120     this.customEditors = this.customEditors || {};
52121 };
52122 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
52123     
52124      /**
52125      * @cfg {Object} customEditors map of colnames=> custom editors.
52126      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
52127      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
52128      * false disables editing of the field.
52129          */
52130     
52131       /**
52132      * @cfg {Object} propertyNames map of property Names to their displayed value
52133          */
52134     
52135     render : function(){
52136         Roo.grid.PropertyGrid.superclass.render.call(this);
52137         this.autoSize.defer(100, this);
52138     },
52139
52140     autoSize : function(){
52141         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52142         if(this.view){
52143             this.view.fitColumns();
52144         }
52145     },
52146
52147     onColumnResize : function(){
52148         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52149         this.autoSize();
52150     },
52151     /**
52152      * Sets the data for the Grid
52153      * accepts a Key => Value object of all the elements avaiable.
52154      * @param {Object} data  to appear in grid.
52155      */
52156     setSource : function(source){
52157         this.store.setSource(source);
52158         //this.autoSize();
52159     },
52160     /**
52161      * Gets all the data from the grid.
52162      * @return {Object} data  data stored in grid
52163      */
52164     getSource : function(){
52165         return this.store.getSource();
52166     }
52167 });/*
52168  * Based on:
52169  * Ext JS Library 1.1.1
52170  * Copyright(c) 2006-2007, Ext JS, LLC.
52171  *
52172  * Originally Released Under LGPL - original licence link has changed is not relivant.
52173  *
52174  * Fork - LGPL
52175  * <script type="text/javascript">
52176  */
52177  
52178 /**
52179  * @class Roo.LoadMask
52180  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52181  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52182  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52183  * element's UpdateManager load indicator and will be destroyed after the initial load.
52184  * @constructor
52185  * Create a new LoadMask
52186  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52187  * @param {Object} config The config object
52188  */
52189 Roo.LoadMask = function(el, config){
52190     this.el = Roo.get(el);
52191     Roo.apply(this, config);
52192     if(this.store){
52193         this.store.on('beforeload', this.onBeforeLoad, this);
52194         this.store.on('load', this.onLoad, this);
52195         this.store.on('loadexception', this.onLoadException, this);
52196         this.removeMask = false;
52197     }else{
52198         var um = this.el.getUpdateManager();
52199         um.showLoadIndicator = false; // disable the default indicator
52200         um.on('beforeupdate', this.onBeforeLoad, this);
52201         um.on('update', this.onLoad, this);
52202         um.on('failure', this.onLoad, this);
52203         this.removeMask = true;
52204     }
52205 };
52206
52207 Roo.LoadMask.prototype = {
52208     /**
52209      * @cfg {Boolean} removeMask
52210      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52211      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52212      */
52213     /**
52214      * @cfg {String} msg
52215      * The text to display in a centered loading message box (defaults to 'Loading...')
52216      */
52217     msg : 'Loading...',
52218     /**
52219      * @cfg {String} msgCls
52220      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52221      */
52222     msgCls : 'x-mask-loading',
52223
52224     /**
52225      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52226      * @type Boolean
52227      */
52228     disabled: false,
52229
52230     /**
52231      * Disables the mask to prevent it from being displayed
52232      */
52233     disable : function(){
52234        this.disabled = true;
52235     },
52236
52237     /**
52238      * Enables the mask so that it can be displayed
52239      */
52240     enable : function(){
52241         this.disabled = false;
52242     },
52243     
52244     onLoadException : function()
52245     {
52246         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52247             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52248         }
52249         this.el.unmask(this.removeMask);
52250     },
52251     // private
52252     onLoad : function()
52253     {
52254         this.el.unmask(this.removeMask);
52255     },
52256
52257     // private
52258     onBeforeLoad : function(){
52259         if(!this.disabled){
52260             this.el.mask(this.msg, this.msgCls);
52261         }
52262     },
52263
52264     // private
52265     destroy : function(){
52266         if(this.store){
52267             this.store.un('beforeload', this.onBeforeLoad, this);
52268             this.store.un('load', this.onLoad, this);
52269             this.store.un('loadexception', this.onLoadException, this);
52270         }else{
52271             var um = this.el.getUpdateManager();
52272             um.un('beforeupdate', this.onBeforeLoad, this);
52273             um.un('update', this.onLoad, this);
52274             um.un('failure', this.onLoad, this);
52275         }
52276     }
52277 };/*
52278  * Based on:
52279  * Ext JS Library 1.1.1
52280  * Copyright(c) 2006-2007, Ext JS, LLC.
52281  *
52282  * Originally Released Under LGPL - original licence link has changed is not relivant.
52283  *
52284  * Fork - LGPL
52285  * <script type="text/javascript">
52286  */
52287 Roo.XTemplate = function(){
52288     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52289     var s = this.html;
52290
52291     s = ['<tpl>', s, '</tpl>'].join('');
52292
52293     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52294
52295     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52296     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52297     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52298     var m, id = 0;
52299     var tpls = [];
52300
52301     while(m = s.match(re)){
52302        var m2 = m[0].match(nameRe);
52303        var m3 = m[0].match(ifRe);
52304        var m4 = m[0].match(execRe);
52305        var exp = null, fn = null, exec = null;
52306        var name = m2 && m2[1] ? m2[1] : '';
52307        if(m3){
52308            exp = m3 && m3[1] ? m3[1] : null;
52309            if(exp){
52310                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52311            }
52312        }
52313        if(m4){
52314            exp = m4 && m4[1] ? m4[1] : null;
52315            if(exp){
52316                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52317            }
52318        }
52319        if(name){
52320            switch(name){
52321                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52322                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52323                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52324            }
52325        }
52326        tpls.push({
52327             id: id,
52328             target: name,
52329             exec: exec,
52330             test: fn,
52331             body: m[1]||''
52332         });
52333        s = s.replace(m[0], '{xtpl'+ id + '}');
52334        ++id;
52335     }
52336     for(var i = tpls.length-1; i >= 0; --i){
52337         this.compileTpl(tpls[i]);
52338     }
52339     this.master = tpls[tpls.length-1];
52340     this.tpls = tpls;
52341 };
52342 Roo.extend(Roo.XTemplate, Roo.Template, {
52343
52344     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52345
52346     applySubTemplate : function(id, values, parent){
52347         var t = this.tpls[id];
52348         if(t.test && !t.test.call(this, values, parent)){
52349             return '';
52350         }
52351         if(t.exec && t.exec.call(this, values, parent)){
52352             return '';
52353         }
52354         var vs = t.target ? t.target.call(this, values, parent) : values;
52355         parent = t.target ? values : parent;
52356         if(t.target && vs instanceof Array){
52357             var buf = [];
52358             for(var i = 0, len = vs.length; i < len; i++){
52359                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52360             }
52361             return buf.join('');
52362         }
52363         return t.compiled.call(this, vs, parent);
52364     },
52365
52366     compileTpl : function(tpl){
52367         var fm = Roo.util.Format;
52368         var useF = this.disableFormats !== true;
52369         var sep = Roo.isGecko ? "+" : ",";
52370         var fn = function(m, name, format, args){
52371             if(name.substr(0, 4) == 'xtpl'){
52372                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52373             }
52374             var v;
52375             if(name.indexOf('.') != -1){
52376                 v = name;
52377             }else{
52378                 v = "values['" + name + "']";
52379             }
52380             if(format && useF){
52381                 args = args ? ',' + args : "";
52382                 if(format.substr(0, 5) != "this."){
52383                     format = "fm." + format + '(';
52384                 }else{
52385                     format = 'this.call("'+ format.substr(5) + '", ';
52386                     args = ", values";
52387                 }
52388             }else{
52389                 args= ''; format = "("+v+" === undefined ? '' : ";
52390             }
52391             return "'"+ sep + format + v + args + ")"+sep+"'";
52392         };
52393         var body;
52394         // branched to use + in gecko and [].join() in others
52395         if(Roo.isGecko){
52396             body = "tpl.compiled = function(values, parent){ return '" +
52397                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52398                     "';};";
52399         }else{
52400             body = ["tpl.compiled = function(values, parent){ return ['"];
52401             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52402             body.push("'].join('');};");
52403             body = body.join('');
52404         }
52405         /** eval:var:zzzzzzz */
52406         eval(body);
52407         return this;
52408     },
52409
52410     applyTemplate : function(values){
52411         return this.master.compiled.call(this, values, {});
52412         var s = this.subs;
52413     },
52414
52415     apply : function(){
52416         return this.applyTemplate.apply(this, arguments);
52417     },
52418
52419     compile : function(){return this;}
52420 });
52421
52422 Roo.XTemplate.from = function(el){
52423     el = Roo.getDom(el);
52424     return new Roo.XTemplate(el.value || el.innerHTML);
52425 };/*
52426  * Original code for Roojs - LGPL
52427  * <script type="text/javascript">
52428  */
52429  
52430 /**
52431  * @class Roo.XComponent
52432  * A delayed Element creator...
52433  * Or a way to group chunks of interface together.
52434  * 
52435  * Mypart.xyx = new Roo.XComponent({
52436
52437     parent : 'Mypart.xyz', // empty == document.element.!!
52438     order : '001',
52439     name : 'xxxx'
52440     region : 'xxxx'
52441     disabled : function() {} 
52442      
52443     tree : function() { // return an tree of xtype declared components
52444         var MODULE = this;
52445         return 
52446         {
52447             xtype : 'NestedLayoutPanel',
52448             // technicall
52449         }
52450      ]
52451  *})
52452  *
52453  *
52454  * It can be used to build a big heiracy, with parent etc.
52455  * or you can just use this to render a single compoent to a dom element
52456  * MYPART.render(Roo.Element | String(id) | dom_element )
52457  * 
52458  * @extends Roo.util.Observable
52459  * @constructor
52460  * @param cfg {Object} configuration of component
52461  * 
52462  */
52463 Roo.XComponent = function(cfg) {
52464     Roo.apply(this, cfg);
52465     this.addEvents({ 
52466         /**
52467              * @event built
52468              * Fires when this the componnt is built
52469              * @param {Roo.XComponent} c the component
52470              */
52471         'built' : true
52472         
52473     });
52474     this.region = this.region || 'center'; // default..
52475     Roo.XComponent.register(this);
52476     this.modules = false;
52477     this.el = false; // where the layout goes..
52478     
52479     
52480 }
52481 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52482     /**
52483      * @property el
52484      * The created element (with Roo.factory())
52485      * @type {Roo.Layout}
52486      */
52487     el  : false,
52488     
52489     /**
52490      * @property el
52491      * for BC  - use el in new code
52492      * @type {Roo.Layout}
52493      */
52494     panel : false,
52495     
52496     /**
52497      * @property layout
52498      * for BC  - use el in new code
52499      * @type {Roo.Layout}
52500      */
52501     layout : false,
52502     
52503      /**
52504      * @cfg {Function|boolean} disabled
52505      * If this module is disabled by some rule, return true from the funtion
52506      */
52507     disabled : false,
52508     
52509     /**
52510      * @cfg {String} parent 
52511      * Name of parent element which it get xtype added to..
52512      */
52513     parent: false,
52514     
52515     /**
52516      * @cfg {String} order
52517      * Used to set the order in which elements are created (usefull for multiple tabs)
52518      */
52519     
52520     order : false,
52521     /**
52522      * @cfg {String} name
52523      * String to display while loading.
52524      */
52525     name : false,
52526     /**
52527      * @cfg {String} region
52528      * Region to render component to (defaults to center)
52529      */
52530     region : 'center',
52531     
52532     /**
52533      * @cfg {Array} items
52534      * A single item array - the first element is the root of the tree..
52535      * It's done this way to stay compatible with the Xtype system...
52536      */
52537     items : false,
52538     
52539     /**
52540      * @property _tree
52541      * The method that retuns the tree of parts that make up this compoennt 
52542      * @type {function}
52543      */
52544     _tree  : false,
52545     
52546      /**
52547      * render
52548      * render element to dom or tree
52549      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52550      */
52551     
52552     render : function(el)
52553     {
52554         
52555         el = el || false;
52556         var hp = this.parent ? 1 : 0;
52557         
52558         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52559             // if parent is a '#.....' string, then let's use that..
52560             var ename = this.parent.substr(1)
52561             this.parent = false;
52562             el = Roo.get(ename);
52563             if (!el) {
52564                 Roo.log("Warning - element can not be found :#" + ename );
52565                 return;
52566             }
52567         }
52568         
52569         
52570         if (!this.parent) {
52571             
52572             el = el ? Roo.get(el) : false;      
52573             
52574             // it's a top level one..
52575             this.parent =  {
52576                 el : new Roo.BorderLayout(el || document.body, {
52577                 
52578                      center: {
52579                          titlebar: false,
52580                          autoScroll:false,
52581                          closeOnTab: true,
52582                          tabPosition: 'top',
52583                           //resizeTabs: true,
52584                          alwaysShowTabs: el && hp? false :  true,
52585                          hideTabs: el || !hp ? true :  false,
52586                          minTabWidth: 140
52587                      }
52588                  })
52589             }
52590         }
52591         
52592                 
52593                 // The 'tree' method is  '_tree now' 
52594             
52595         var tree = this._tree ? this._tree() : this.tree();
52596         tree.region = tree.region || this.region;
52597         this.el = this.parent.el.addxtype(tree);
52598         this.fireEvent('built', this);
52599         
52600         this.panel = this.el;
52601         this.layout = this.panel.layout;
52602                 this.parentLayout = this.parent.layout  || false;  
52603          
52604     }
52605     
52606 });
52607
52608 Roo.apply(Roo.XComponent, {
52609     
52610     /**
52611      * @property  buildCompleted
52612      * True when the builder has completed building the interface.
52613      * @type Boolean
52614      */
52615     buildCompleted : false,
52616      
52617     /**
52618      * @property  topModule
52619      * the upper most module - uses document.element as it's constructor.
52620      * @type Object
52621      */
52622      
52623     topModule  : false,
52624       
52625     /**
52626      * @property  modules
52627      * array of modules to be created by registration system.
52628      * @type {Array} of Roo.XComponent
52629      */
52630     
52631     modules : [],
52632     /**
52633      * @property  elmodules
52634      * array of modules to be created by which use #ID 
52635      * @type {Array} of Roo.XComponent
52636      */
52637      
52638     elmodules : [],
52639
52640     
52641     /**
52642      * Register components to be built later.
52643      *
52644      * This solves the following issues
52645      * - Building is not done on page load, but after an authentication process has occured.
52646      * - Interface elements are registered on page load
52647      * - Parent Interface elements may not be loaded before child, so this handles that..
52648      * 
52649      *
52650      * example:
52651      * 
52652      * MyApp.register({
52653           order : '000001',
52654           module : 'Pman.Tab.projectMgr',
52655           region : 'center',
52656           parent : 'Pman.layout',
52657           disabled : false,  // or use a function..
52658         })
52659      
52660      * * @param {Object} details about module
52661      */
52662     register : function(obj) {
52663                 
52664                 Roo.XComponent.event.fireEvent('register', obj);
52665                 switch(typeof(obj.disabled) ) {
52666                         
52667                         case 'undefined':
52668                                 break;
52669                         
52670                         case 'function':
52671                                 if ( obj.disabled() ) {
52672                                         return;
52673                                 }
52674                                 break;
52675                         default:
52676                                 if (obj.disabled) {
52677                                         return;
52678                                 }
52679                                 break;
52680                 }
52681                 
52682         this.modules.push(obj);
52683          
52684     },
52685     /**
52686      * convert a string to an object..
52687      * eg. 'AAA.BBB' -> finds AAA.BBB
52688
52689      */
52690     
52691     toObject : function(str)
52692     {
52693         if (!str || typeof(str) == 'object') {
52694             return str;
52695         }
52696         if (str.substring(0,1) == '#') {
52697             return str;
52698         }
52699
52700         var ar = str.split('.');
52701         var rt, o;
52702         rt = ar.shift();
52703             /** eval:var:o */
52704         try {
52705             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52706         } catch (e) {
52707             throw "Module not found : " + str;
52708         }
52709         
52710         if (o === false) {
52711             throw "Module not found : " + str;
52712         }
52713         Roo.each(ar, function(e) {
52714             if (typeof(o[e]) == 'undefined') {
52715                 throw "Module not found : " + str;
52716             }
52717             o = o[e];
52718         });
52719         
52720         return o;
52721         
52722     },
52723     
52724     
52725     /**
52726      * move modules into their correct place in the tree..
52727      * 
52728      */
52729     preBuild : function ()
52730     {
52731         var _t = this;
52732         Roo.each(this.modules , function (obj)
52733         {
52734             var opar = obj.parent;
52735             try { 
52736                 obj.parent = this.toObject(opar);
52737             } catch(e) {
52738                 Roo.log("parent:toObject failed: " + e.toString());
52739                 return;
52740             }
52741             
52742             if (!obj.parent) {
52743                                 Roo.debug && Roo.log("GOT top level module");
52744                                 Roo.debug && Roo.log(obj);
52745                                 obj.modules = new Roo.util.MixedCollection(false, 
52746                     function(o) { return o.order + '' }
52747                 );
52748                 this.topModule = obj;
52749                 return;
52750             }
52751                         // parent is a string (usually a dom element name..)
52752             if (typeof(obj.parent) == 'string') {
52753                 this.elmodules.push(obj);
52754                 return;
52755             }
52756             if (obj.parent.constructor != Roo.XComponent) {
52757                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
52758             }
52759             if (!obj.parent.modules) {
52760                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52761                     function(o) { return o.order + '' }
52762                 );
52763             }
52764             
52765             obj.parent.modules.add(obj);
52766         }, this);
52767     },
52768     
52769      /**
52770      * make a list of modules to build.
52771      * @return {Array} list of modules. 
52772      */ 
52773     
52774     buildOrder : function()
52775     {
52776         var _this = this;
52777         var cmp = function(a,b) {   
52778             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52779         };
52780         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52781             throw "No top level modules to build";
52782         }
52783         
52784         // make a flat list in order of modules to build.
52785         var mods = this.topModule ? [ this.topModule ] : [];
52786                 
52787                 // elmodules (is a list of DOM based modules )
52788         Roo.each(this.elmodules,function(e) { mods.push(e) });
52789
52790         
52791         // add modules to their parents..
52792         var addMod = function(m) {
52793                         Roo.debug && Roo.log("build Order: add: " + m.name);
52794             
52795             mods.push(m);
52796             if (m.modules) {
52797                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
52798                 m.modules.keySort('ASC',  cmp );
52799                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
52800
52801                 m.modules.each(addMod);
52802             } else {
52803                                 Roo.debug && Roo.log("build Order: no child modules");
52804                         }
52805             // not sure if this is used any more..
52806             if (m.finalize) {
52807                 m.finalize.name = m.name + " (clean up) ";
52808                 mods.push(m.finalize);
52809             }
52810             
52811         }
52812         if (this.topModule) { 
52813             this.topModule.modules.keySort('ASC',  cmp );
52814             this.topModule.modules.each(addMod);
52815         }
52816         return mods;
52817     },
52818     
52819      /**
52820      * Build the registered modules.
52821      * @param {Object} parent element.
52822      * @param {Function} optional method to call after module has been added.
52823      * 
52824      */ 
52825    
52826     build : function() 
52827     {
52828         
52829         this.preBuild();
52830         var mods = this.buildOrder();
52831       
52832         //this.allmods = mods;
52833         //Roo.debug && Roo.log(mods);
52834         //return;
52835         if (!mods.length) { // should not happen
52836             throw "NO modules!!!";
52837         }
52838         
52839         
52840         var msg = "Building Interface...";
52841         // flash it up as modal - so we store the mask!?
52842         Roo.MessageBox.show({ title: 'loading' });
52843         Roo.MessageBox.show({
52844            title: "Please wait...",
52845            msg: msg,
52846            width:450,
52847            progress:true,
52848            closable:false,
52849            modal: false
52850           
52851         });
52852         var total = mods.length;
52853         
52854         var _this = this;
52855         var progressRun = function() {
52856             if (!mods.length) {
52857                 Roo.debug && Roo.log('hide?');
52858                 Roo.MessageBox.hide();
52859                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
52860                 
52861                 // THE END...
52862                 return false;   
52863             }
52864             
52865             var m = mods.shift();
52866             
52867             
52868             Roo.debug && Roo.log(m);
52869             // not sure if this is supported any more.. - modules that are are just function
52870             if (typeof(m) == 'function') { 
52871                 m.call(this);
52872                 return progressRun.defer(10, _this);
52873             } 
52874             
52875             
52876             msg = "Building Interface " + (total  - mods.length) + 
52877                     " of " + total + 
52878                     (m.name ? (' - ' + m.name) : '');
52879                         Roo.debug && Roo.log(msg);
52880             Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
52881             
52882          
52883             // is the module disabled?
52884             var disabled = (typeof(m.disabled) == 'function') ?
52885                 m.disabled.call(m.module.disabled) : m.disabled;    
52886             
52887             
52888             if (disabled) {
52889                 return progressRun(); // we do not update the display!
52890             }
52891             
52892             // now build 
52893             
52894                         
52895                         
52896             m.render();
52897             // it's 10 on top level, and 1 on others??? why...
52898             return progressRun.defer(10, _this);
52899              
52900         }
52901         progressRun.defer(1, _this);
52902      
52903         
52904         
52905     },
52906         
52907         
52908         /**
52909          * Event Object.
52910          *
52911          *
52912          */
52913         event: false, 
52914     /**
52915          * wrapper for event.on - aliased later..  
52916          * Typically use to register a event handler for register:
52917          *
52918          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
52919          *
52920          */
52921     on : false
52922    
52923     
52924     
52925 });
52926
52927 Roo.XComponent.event = new Roo.util.Observable({
52928                 events : { 
52929                         /**
52930                          * @event register
52931                          * Fires when an Component is registered,
52932                          * set the disable property on the Component to stop registration.
52933                          * @param {Roo.XComponent} c the component being registerd.
52934                          * 
52935                          */
52936                         'register' : true,
52937                         /**
52938                          * @event buildcomplete
52939                          * Fires on the top level element when all elements have been built
52940                          * @param {Roo.XComponent} the top level component.
52941                          */
52942                         'buildcomplete' : true
52943                         
52944                 }
52945 });
52946
52947 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
52948  //<script type="text/javascript">
52949
52950
52951 /**
52952  * @class Roo.Login
52953  * @extends Roo.LayoutDialog
52954  * A generic Login Dialog..... - only one needed in theory!?!?
52955  *
52956  * Fires XComponent builder on success...
52957  * 
52958  * Sends 
52959  *    username,password, lang = for login actions.
52960  *    check = 1 for periodic checking that sesion is valid.
52961  *    passwordRequest = email request password
52962  *    logout = 1 = to logout
52963  * 
52964  * Affects: (this id="????" elements)
52965  *   loading  (removed) (used to indicate application is loading)
52966  *   loading-mask (hides) (used to hide application when it's building loading)
52967  *   
52968  * 
52969  * Usage: 
52970  *    
52971  * 
52972  * Myapp.login = Roo.Login({
52973      url: xxxx,
52974    
52975      realm : 'Myapp', 
52976      
52977      
52978      method : 'POST',
52979      
52980      
52981      * 
52982  })
52983  * 
52984  * 
52985  * 
52986  **/
52987  
52988 Roo.Login = function(cfg)
52989 {
52990     this.addEvents({
52991         'refreshed' : true
52992     });
52993     
52994     Roo.apply(this,cfg);
52995     
52996     Roo.onReady(function() {
52997         this.onLoad();
52998     }, this);
52999     // call parent..
53000     
53001    
53002     Roo.Login.superclass.constructor.call(this, this);
53003     //this.addxtype(this.items[0]);
53004     
53005     
53006 }
53007
53008
53009 Roo.extend(Roo.Login, Roo.LayoutDialog, {
53010     
53011     /**
53012      * @cfg {String} method
53013      * Method used to query for login details.
53014      */
53015     
53016     method : 'POST',
53017     /**
53018      * @cfg {String} url
53019      * URL to query login data. - eg. baseURL + '/Login.php'
53020      */
53021     url : '',
53022     
53023     /**
53024      * @property user
53025      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
53026      * @type {Object} 
53027      */
53028     user : false,
53029     /**
53030      * @property checkFails
53031      * Number of times we have attempted to get authentication check, and failed.
53032      * @type {Number} 
53033      */
53034     checkFails : 0,
53035       /**
53036      * @property intervalID
53037      * The window interval that does the constant login checking.
53038      * @type {Number} 
53039      */
53040     intervalID : 0,
53041     
53042     
53043     onLoad : function() // called on page load...
53044     {
53045         // load 
53046          
53047         if (Roo.get('loading')) { // clear any loading indicator..
53048             Roo.get('loading').remove();
53049         }
53050         
53051         //this.switchLang('en'); // set the language to english..
53052        
53053         this.check({
53054             success:  function(response, opts)  {  // check successfull...
53055             
53056                 var res = this.processResponse(response);
53057                 this.checkFails =0;
53058                 if (!res.success) { // error!
53059                     this.checkFails = 5;
53060                     //console.log('call failure');
53061                     return this.failure(response,opts);
53062                 }
53063                 
53064                 if (!res.data.id) { // id=0 == login failure.
53065                     return this.show();
53066                 }
53067                 
53068                               
53069                         //console.log(success);
53070                 this.fillAuth(res.data);   
53071                 this.checkFails =0;
53072                 Roo.XComponent.build();
53073             },
53074             failure : this.show
53075         });
53076         
53077     }, 
53078     
53079     
53080     check: function(cfg) // called every so often to refresh cookie etc..
53081     {
53082         if (cfg.again) { // could be undefined..
53083             this.checkFails++;
53084         } else {
53085             this.checkFails = 0;
53086         }
53087         var _this = this;
53088         if (this.sending) {
53089             if ( this.checkFails > 4) {
53090                 Roo.MessageBox.alert("Error",  
53091                     "Error getting authentication status. - try reloading, or wait a while", function() {
53092                         _this.sending = false;
53093                     }); 
53094                 return;
53095             }
53096             cfg.again = true;
53097             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
53098             return;
53099         }
53100         this.sending = true;
53101         
53102         Roo.Ajax.request({  
53103             url: this.url,
53104             params: {
53105                 getAuthUser: true
53106             },  
53107             method: this.method,
53108             success:  cfg.success || this.success,
53109             failure : cfg.failure || this.failure,
53110             scope : this,
53111             callCfg : cfg
53112               
53113         });  
53114     }, 
53115     
53116     
53117     logout: function()
53118     {
53119         window.onbeforeunload = function() { }; // false does not work for IE..
53120         this.user = false;
53121         var _this = this;
53122         
53123         Roo.Ajax.request({  
53124             url: this.url,
53125             params: {
53126                 logout: 1
53127             },  
53128             method: 'GET',
53129             failure : function() {
53130                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
53131                     document.location = document.location.toString() + '?ts=' + Math.random();
53132                 });
53133                 
53134             },
53135             success : function() {
53136                 _this.user = false;
53137                 this.checkFails =0;
53138                 // fixme..
53139                 document.location = document.location.toString() + '?ts=' + Math.random();
53140             }
53141               
53142               
53143         }); 
53144     },
53145     
53146     processResponse : function (response)
53147     {
53148         var res = '';
53149         try {
53150             res = Roo.decode(response.responseText);
53151             // oops...
53152             if (typeof(res) != 'object') {
53153                 res = { success : false, errorMsg : res, errors : true };
53154             }
53155             if (typeof(res.success) == 'undefined') {
53156                 res.success = false;
53157             }
53158             
53159         } catch(e) {
53160             res = { success : false,  errorMsg : response.responseText, errors : true };
53161         }
53162         return res;
53163     },
53164     
53165     success : function(response, opts)  // check successfull...
53166     {  
53167         this.sending = false;
53168         var res = this.processResponse(response);
53169         if (!res.success) {
53170             return this.failure(response, opts);
53171         }
53172         if (!res.data || !res.data.id) {
53173             return this.failure(response,opts);
53174         }
53175         //console.log(res);
53176         this.fillAuth(res.data);
53177         
53178         this.checkFails =0;
53179         
53180     },
53181     
53182     
53183     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
53184     {
53185         this.authUser = -1;
53186         this.sending = false;
53187         var res = this.processResponse(response);
53188         //console.log(res);
53189         if ( this.checkFails > 2) {
53190         
53191             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
53192                 "Error getting authentication status. - try reloading"); 
53193             return;
53194         }
53195         opts.callCfg.again = true;
53196         this.check.defer(1000, this, [ opts.callCfg ]);
53197         return;  
53198     },
53199     
53200     
53201     
53202     fillAuth: function(au) {
53203         this.startAuthCheck();
53204         this.authUserId = au.id;
53205         this.authUser = au;
53206         this.lastChecked = new Date();
53207         this.fireEvent('refreshed', au);
53208         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
53209         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
53210         au.lang = au.lang || 'en';
53211         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53212         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53213         this.switchLang(au.lang );
53214         
53215      
53216         // open system... - -on setyp..
53217         if (this.authUserId  < 0) {
53218             Roo.MessageBox.alert("Warning", 
53219                 "This is an open system - please set up a admin user with a password.");  
53220         }
53221          
53222         //Pman.onload(); // which should do nothing if it's a re-auth result...
53223         
53224              
53225     },
53226     
53227     startAuthCheck : function() // starter for timeout checking..
53228     {
53229         if (this.intervalID) { // timer already in place...
53230             return false;
53231         }
53232         var _this = this;
53233         this.intervalID =  window.setInterval(function() {
53234               _this.check(false);
53235             }, 120000); // every 120 secs = 2mins..
53236         
53237         
53238     },
53239          
53240     
53241     switchLang : function (lang) 
53242     {
53243         _T = typeof(_T) == 'undefined' ? false : _T;
53244           if (!_T || !lang.length) {
53245             return;
53246         }
53247         
53248         if (!_T && lang != 'en') {
53249             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53250             return;
53251         }
53252         
53253         if (typeof(_T.en) == 'undefined') {
53254             _T.en = {};
53255             Roo.apply(_T.en, _T);
53256         }
53257         
53258         if (typeof(_T[lang]) == 'undefined') {
53259             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53260             return;
53261         }
53262         
53263         
53264         Roo.apply(_T, _T[lang]);
53265         // just need to set the text values for everything...
53266         var _this = this;
53267         /* this will not work ...
53268         if (this.form) { 
53269             
53270                
53271             function formLabel(name, val) {
53272                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53273             }
53274             
53275             formLabel('password', "Password"+':');
53276             formLabel('username', "Email Address"+':');
53277             formLabel('lang', "Language"+':');
53278             this.dialog.setTitle("Login");
53279             this.dialog.buttons[0].setText("Forgot Password");
53280             this.dialog.buttons[1].setText("Login");
53281         }
53282         */
53283         
53284         
53285     },
53286     
53287     
53288     title: "Login",
53289     modal: true,
53290     width:  350,
53291     //height: 230,
53292     height: 180,
53293     shadow: true,
53294     minWidth:200,
53295     minHeight:180,
53296     //proxyDrag: true,
53297     closable: false,
53298     draggable: false,
53299     collapsible: false,
53300     resizable: false,
53301     center: {  // needed??
53302         autoScroll:false,
53303         titlebar: false,
53304        // tabPosition: 'top',
53305         hideTabs: true,
53306         closeOnTab: true,
53307         alwaysShowTabs: false
53308     } ,
53309     listeners : {
53310         
53311         show  : function(dlg)
53312         {
53313             //console.log(this);
53314             this.form = this.layout.getRegion('center').activePanel.form;
53315             this.form.dialog = dlg;
53316             this.buttons[0].form = this.form;
53317             this.buttons[0].dialog = dlg;
53318             this.buttons[1].form = this.form;
53319             this.buttons[1].dialog = dlg;
53320            
53321            //this.resizeToLogo.defer(1000,this);
53322             // this is all related to resizing for logos..
53323             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53324            //// if (!sz) {
53325              //   this.resizeToLogo.defer(1000,this);
53326              //   return;
53327            // }
53328             //var w = Ext.lib.Dom.getViewWidth() - 100;
53329             //var h = Ext.lib.Dom.getViewHeight() - 100;
53330             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53331             //this.center();
53332             if (this.disabled) {
53333                 this.hide();
53334                 return;
53335             }
53336             
53337             if (this.user.id < 0) { // used for inital setup situations.
53338                 return;
53339             }
53340             
53341             if (this.intervalID) {
53342                 // remove the timer
53343                 window.clearInterval(this.intervalID);
53344                 this.intervalID = false;
53345             }
53346             
53347             
53348             if (Roo.get('loading')) {
53349                 Roo.get('loading').remove();
53350             }
53351             if (Roo.get('loading-mask')) {
53352                 Roo.get('loading-mask').hide();
53353             }
53354             
53355             //incomming._node = tnode;
53356             this.form.reset();
53357             //this.dialog.modal = !modal;
53358             //this.dialog.show();
53359             this.el.unmask(); 
53360             
53361             
53362             this.form.setValues({
53363                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53364                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53365             });
53366             
53367             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53368             if (this.form.findField('username').getValue().length > 0 ){
53369                 this.form.findField('password').focus();
53370             } else {
53371                this.form.findField('username').focus();
53372             }
53373     
53374         }
53375     },
53376     items : [
53377          {
53378        
53379             xtype : 'ContentPanel',
53380             xns : Roo,
53381             region: 'center',
53382             fitToFrame : true,
53383             
53384             items : [
53385     
53386                 {
53387                
53388                     xtype : 'Form',
53389                     xns : Roo.form,
53390                     labelWidth: 100,
53391                     style : 'margin: 10px;',
53392                     
53393                     listeners : {
53394                         actionfailed : function(f, act) {
53395                             // form can return { errors: .... }
53396                                 
53397                             //act.result.errors // invalid form element list...
53398                             //act.result.errorMsg// invalid form element list...
53399                             
53400                             this.dialog.el.unmask();
53401                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53402                                         "Login failed - communication error - try again.");
53403                                       
53404                         },
53405                         actioncomplete: function(re, act) {
53406                              
53407                             Roo.state.Manager.set(
53408                                 this.dialog.realm + '.username',  
53409                                     this.findField('username').getValue()
53410                             );
53411                             Roo.state.Manager.set(
53412                                 this.dialog.realm + '.lang',  
53413                                 this.findField('lang').getValue() 
53414                             );
53415                             
53416                             this.dialog.fillAuth(act.result.data);
53417                               
53418                             this.dialog.hide();
53419                             
53420                             if (Roo.get('loading-mask')) {
53421                                 Roo.get('loading-mask').show();
53422                             }
53423                             Roo.XComponent.build();
53424                             
53425                              
53426                             
53427                         }
53428                     },
53429                     items : [
53430                         {
53431                             xtype : 'TextField',
53432                             xns : Roo.form,
53433                             fieldLabel: "Email Address",
53434                             name: 'username',
53435                             width:200,
53436                             autoCreate : {tag: "input", type: "text", size: "20"}
53437                         },
53438                         {
53439                             xtype : 'TextField',
53440                             xns : Roo.form,
53441                             fieldLabel: "Password",
53442                             inputType: 'password',
53443                             name: 'password',
53444                             width:200,
53445                             autoCreate : {tag: "input", type: "text", size: "20"},
53446                             listeners : {
53447                                 specialkey : function(e,ev) {
53448                                     if (ev.keyCode == 13) {
53449                                         this.form.dialog.el.mask("Logging in");
53450                                         this.form.doAction('submit', {
53451                                             url: this.form.dialog.url,
53452                                             method: this.form.dialog.method
53453                                         });
53454                                     }
53455                                 }
53456                             }  
53457                         },
53458                         {
53459                             xtype : 'ComboBox',
53460                             xns : Roo.form,
53461                             fieldLabel: "Language",
53462                             name : 'langdisp',
53463                             store: {
53464                                 xtype : 'SimpleStore',
53465                                 fields: ['lang', 'ldisp'],
53466                                 data : [
53467                                     [ 'en', 'English' ],
53468                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53469                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53470                                 ]
53471                             },
53472                             
53473                             valueField : 'lang',
53474                             hiddenName:  'lang',
53475                             width: 200,
53476                             displayField:'ldisp',
53477                             typeAhead: false,
53478                             editable: false,
53479                             mode: 'local',
53480                             triggerAction: 'all',
53481                             emptyText:'Select a Language...',
53482                             selectOnFocus:true,
53483                             listeners : {
53484                                 select :  function(cb, rec, ix) {
53485                                     this.form.switchLang(rec.data.lang);
53486                                 }
53487                             }
53488                         
53489                         }
53490                     ]
53491                 }
53492                   
53493                 
53494             ]
53495         }
53496     ],
53497     buttons : [
53498         {
53499             xtype : 'Button',
53500             xns : 'Roo',
53501             text : "Forgot Password",
53502             listeners : {
53503                 click : function() {
53504                     //console.log(this);
53505                     var n = this.form.findField('username').getValue();
53506                     if (!n.length) {
53507                         Roo.MessageBox.alert("Error", "Fill in your email address");
53508                         return;
53509                     }
53510                     Roo.Ajax.request({
53511                         url: this.dialog.url,
53512                         params: {
53513                             passwordRequest: n
53514                         },
53515                         method: this.dialog.method,
53516                         success:  function(response, opts)  {  // check successfull...
53517                         
53518                             var res = this.dialog.processResponse(response);
53519                             if (!res.success) { // error!
53520                                Roo.MessageBox.alert("Error" ,
53521                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53522                                return;
53523                             }
53524                             Roo.MessageBox.alert("Notice" ,
53525                                 "Please check you email for the Password Reset message");
53526                         },
53527                         failure : function() {
53528                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53529                         }
53530                         
53531                     });
53532                 }
53533             }
53534         },
53535         {
53536             xtype : 'Button',
53537             xns : 'Roo',
53538             text : "Login",
53539             listeners : {
53540                 
53541                 click : function () {
53542                         
53543                     this.dialog.el.mask("Logging in");
53544                     this.form.doAction('submit', {
53545                             url: this.dialog.url,
53546                             method: this.dialog.method
53547                     });
53548                 }
53549             }
53550         }
53551     ]
53552   
53553   
53554 })
53555  
53556
53557
53558